Not Idiomatic Haskell Code
The many1
function code is the same of the Alternative.many
function in the Haskell prelude. The scope of the function is applying zero, one or more times its applicative argument.
{-# LANGUAGE ApplicativeDo #-}
import Control.Applicative
class Alternative f => Alternative1 f where
-- | Zero or more.
many1 :: f a -> f [a]
many1 v = many_v
where
many_v = some_v <|> pure []
some_v = (fmap (:) v) <*> many_v
For me, the some_v
inner function is hard to read/comprehend.
Idiomatic Haskell Code
We can rewrite some_v
in this way
class Alternative f => Alternative2 f where
-- | Zero or more.
many2 :: f a -> f [a]
many2 v = many_v
where
many_v = some_v <|> pure []
some_v = (:) <$> v <*> many_v
some_v
is rewritten using the Applicative
idiomatic form:
- we evaluate
v
andmany_v
using the specificf
Applicative
instance rules, and inside the applicative running context - we use the two results as arguments of the plain Haskell function
(:)
- we put the final result of
(:)
function in thef
Functor
The majority of Applicative
usage patterns follow this syntactic pattern, or its variation pure f <*> argument1 <*> argument2 <*> ...
and after some accustomization, it can be comprehended intuitively without thinking too much to the type definitions, and to the details of involved functional combinators. This is the same for the code like case1 <|> case2
, where we recognize immediately that <|>
is separating two choices, and we understand the meaning without thinking too much to the underling details.
So we have improved the readability of the first version of the code using the common syntactic form (:) <$> ... <*> ...
, instead of the less used fmap (:) <*> ... <*> ...
form.
ApplicativeDo
This is a variant of many
, but with ApplicativeDo
syntax.
class Alternative f => Alternative3 f where
-- | Zero or more.
many3 :: f a -> f [a]
many3 v = some_v <|> pure []
where
some_v = do
v' <- v
v'' <- (some_v <|> pure [])
return (v':v'')
The ApplicativeDo
notation helps understanding the semantic of some_v
because all function arguments are explicit. But the code is a little more verbose respect the idiomatic Applicative pattern usage.
Functional Code
We can rewrite many
using explicit types, and adding comments. The resulting code is too much verbose, and not intuitively readable, because there are too much low level details about types and functional combinators, hiding its true meaning.
class Alternative f => Alternative4 f where
-- | Zero or more.
many4 :: f a -> f [a]
many4 v = many_v
where
-- TYPE: many_v :: f [a]
many_v = some_v <|> pure []
-- if `some_v` fails, return empty list,
-- terminating `some_v/many_v` mutual recursion.
-- TYPE: some_v :: f [a]
some_v
= let -- TYPE: fa :: a -> ([a] -> [a])
fa a = \as -> a:as
-- `a` is the current result,
-- to concatenate with next results,
-- supplied in the [2] part.
-- TYPE: fm :: f ([a] -> [a])
fm = fmap fa v
-- create something nice to combine using
-- `<*>` `Applicative` combinator,
-- having as specific types:
-- `(<*>) :: f ([a] -> [a]) -> f [a] -> f [a]`
-- `fmap :: ([a] -> [a]) -> f [a] -> f [a]
in fm <*> many_v
-- [2] part:
-- concatenate the current result with the next results in `many_v`.
-- This is a mutual recursion between `many_v` and `some_v`.
-- At some point `many_v` will fail returning the empty list,
-- and the recursion loop will stop.
Personal Conclusions
Haskell gives a lot of freedom in the way we can express things, but this freedom can harm code readability. Haskell code using Monads and Applicative instances, should follow few known and accepted idiomatic forms, because otherwise understanding the meaning of complex function combinations is not easy.
All complex function combinations in the end are only function calls, but human mind (at least my mind) does not reason easily with second order functions combined toghether in fancy ways. Instead we are able to recognize common syntactic forms like f <$> arg1 <*> arg2
, associating immediately to them a semantic meaning, independently from the more complex low level details.
No comments:
Post a Comment