Reputation: 6368
While doing exercises of chapter 3 of the Purescript by Example book was puzzled by this:
The exercise is to write an isInBook :: String -> String -> AddressBook -> Boolean
findEntryByName :: String -> String -> AddressBook -> Maybe Entry
findEntryByName first last = head <<< filter
\entry -> entry.firstName == first && entry.lastName == last
isSomething :: forall a. Maybe a -> Boolean
isSomething a = case a of
Nothing -> false
Just _ -> true
AddressBook :: List Entry
, and this does what I want it to do. (on a sidenote, is there a common name for isSomething
? It seems like a common function to want; a proposition whether something isn't Nothing
)
But this doesn't compile:
isInBook = isSomething <<< findEntryByName
And after some searching I found that this does:
isInBook fist last = isSomething <<< findEntryByName first last
Why? I found the compile error not terribly helpful, but that may be my inexperience:
Could not match type
Function String
with type
Maybe
while trying to match type String
-> List
{ address :: ...
, firstName :: String
, lastName :: String
}
-> Maybe
{ address :: ...
, firstName :: String
, lastName :: String
}
with type Maybe t1
while checking that expression findEntryByName
has type t0 -> Maybe t1
in value declaration isInBook
where t0 is an unknown type
t1 is an unknown type
Upvotes: 0
Views: 51
Reputation: 18249
I agree that the error message that you got wasn't particularly helpful. I'll try to break down why it happens.
Basically it comes down to the type of <<<
, which in the context of functions is:
(<<<) :: (b -> c) -> (a -> b) -> a -> c
So when you do isInBook = isSomething <<< findEntryByName
, since isSomething
has type Maybe a -> Boolean
, we must have that b
in the above "generic" type signature is Maybe a
and c
is Boolean
. But the type of findEntryByName
is String -> String -> AddressBook -> Maybe Entry
, which would mean that a
has to be String
and b
has to be the function type String -> AddressBook -> Maybe Entry
. [Recall here that functions in Purescript are curried, so a -> b -> c
means precisely a -> (b -> c)
.] We have inferred b
to be 2 completely different things, and that is exactly what your error message is complaining about.
Basically <<<
only really works well with functions of one argument. This is what your corrected version does: findEntryByName first last
is a function of type AddressBook -> Maybe Entry
, which composes perfectly with isSomething :: Maybe a -> Boolean
.
You can use it with functions of more than one argument - after all, due to currying all functions in Purescript strictly speaking taking one argument - but as you saw with findEntryByName
, trying to compose something with a function of more than one argument means that your next function itself has to take a function as argument.
If the repetition of first
and last
in the correct expression, isInBook first last = isSomething <<< findEntryByName first last
, bothers you, then you can if you want eta-reduce to get rid of last
:
isInBook first = \last -> isSomething <<< findEntryByName first last
= (isSomething <<< _) <<< (findEntryByName first)
and then do similar with first
to make it fully point-free. But the final expression will be fairly unreadable, and even the above looks much worse to me than the original. Fewer explicit arguments isn't always better.
Oh, and your isSomething
is indeed in the standard libraries, as isJust. It's not actually as commonly useful as you seem to think, because most of the time when you have a Just
value you want to actually extract the underlying value and do something with it, rather than throw information away with a Boolean True
. But well sometimes that is all you want, as in your case, and for those cases the function exists.
Upvotes: 1