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
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:So when you do
isInBook = isSomething <<< findEntryByName, sinceisSomethinghas typeMaybe a -> Boolean, we must have thatbin the above "generic" type signature isMaybe aandcisBoolean. But the type offindEntryByNameisString -> String -> AddressBook -> Maybe Entry, which would mean thatahas to beStringandbhas to be the function typeString -> AddressBook -> Maybe Entry. [Recall here that functions in Purescript are curried, soa -> b -> cmeans preciselya -> (b -> c).] We have inferredbto 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 lastis a function of typeAddressBook -> Maybe Entry, which composes perfectly withisSomething :: 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
firstandlastin the correct expression,isInBook first last = isSomething <<< findEntryByName first last, bothers you, then you can if you want eta-reduce to get rid oflast:and then do similar with
firstto 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
isSomethingis 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 aJustvalue you want to actually extract the underlying value and do something with it, rather than throw information away with a BooleanTrue. But well sometimes that is all you want, as in your case, and for those cases the function exists.