1

This is a self-motivated exercise. To get the final case of <*> to work, I resorted to implementing concatenation as an auxiliary function. Have I missed a trick? That is, could that case have been written without needing an auxiliary function or be written in some other way? (BTW, my principle reference is the Haskell Wikibook, if that's relevant.)

The code:

    data List a = Empty | Item a (List a)
        deriving (Eq, Ord, Show, Read)

    instance Functor List where
        fmap ab Empty       = Empty
        fmap ab (Item a la) = Item (ab a) (fmap ab la)

    instance Applicative List where
        pure a                    = Item a Empty
        Empty       <*> _         = Empty
        _           <*> Empty     = Empty
        Item ab lab <*> Item a la = -- this is the case in question
            Item (ab a) (concatenation (ab <$> la) (lab <*> Item a la))

I guess what it comes down to is that, as far as I can recall in my limited experience, I've not needed an auxiliary function to implement any kind of instance so using one here makes me wonder whether it's necessary ...

Rik
  • 55
  • 7
  • 1
    I assume your `concatenation` function is just your type's version of `(++)`. There's no problem here; whatever else you did would just include a reimplementation of `concatenation` anyway. – chepner Feb 05 '23 at 15:02
  • 1
    (For what it's worth, the `Applicative []` instance in `GHC.Base` "cheats" by using a list comprehension: `fs <*> xs = [f x | f <- fs, x <- xs]`.) – chepner Feb 05 '23 at 15:03
  • Remember, the goal here is to apply `ab` to ever element of `Item a la`; you are applying `ab` to `a` *and* mapping `ab` over `la`. Why not just map `ab` over `Item a la` all at once? (Which is to say, your definition of `<*>` is reimplementing part of `fmap` instead of *just* using `fmap`.) – chepner Feb 05 '23 at 15:12
  • @chepner: yes, your assumption is correct and thanks for the answers. That's interesting about the use of comprehensions! Still chewing over the last bit :) BTW, if you put up a 'big' answer, I'll happily tick that. Thanks again – Rik Feb 05 '23 at 15:25
  • @Rik The last bit is suggesting `Item ab lab <*> la = concatenation (ab <$> la) (lab <*> la)`. (This also lets you reduce the number of cases, as a second clause `Empty <*> _ = Empty` then covers everything in just two clauses instead of three.) – Daniel Wagner Feb 06 '23 at 16:27
  • @DanielWagner: yes, so `Empty <*> _ = Empty` and `Item ab lab <*> la = concatenation (ab <$> la) (lab <*> la)`. Thanks! – Rik Feb 09 '23 at 21:09

2 Answers2

2

This is a shorter, equivalent version of the applicative instance for List.

instance Applicative List where
    pure a             = Item a Empty
    Empty       <*> _  = Empty
    Item ab lab <*> la = concatenation (ab <$> la) (lab <*> la)
Rik
  • 55
  • 7
1

There's no reason to avoid auxiliary functions. However you define the instance, it either needs to use concatenation, or reimplement parts or all of its definition.

chepner
  • 497,756
  • 71
  • 530
  • 681