In this question, the author has written an implementation of de Morgan's laws in Haskell. I understand the implementations of notAandnotB, and notAornotB, but I'm struggling to understand the implementation of notAorB which is:
notAorB :: (Either a b -> c) -> (a -> c, b -> c)
notAorB f = (f . Left, f . Right)
Could someone explain how the (f . Left, f . Right) part works? I've seen the . operator used before, but with three arguments, not two.
Thank you in advance.
Recall that the definition of the . operator is (f . g) x = f (g x), i.e. f . g = \x -> f (g x) (syntactically, it is a binary operator, it is just that Haskell’s syntax sugar permits the latter definition to be restated as the former). So, the definition you have can be rephrased as
notAorB f = ((\x -> f (Left x)), (\y -> f (Right y)))
(this can be done mechanically by Lambdabot on #haskell, tell him to #unpl ‹expr›), or more verbosely
notAorB f = (lt, rt)
where lt x = f (Left x)
rt y = f (Right y)
As always, try writing down the types. If (.) :: ∀ s t u. (t -> u) -> (s -> t) -> s -> u, f :: Either a b -> c, Left :: ∀ p q. p -> Either p q, then f . Left or (.) f Left :: a -> c, etc.
Related
I'm going through the new book Haskell Programming from First Principles. It seems decent, but I feel that there are some confusing holes in the explanations. I apologize if I'm missing something basic.
The last problem in chapter 5 is to fill in the ??? below so that things make sense:
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge = ???
The solution which was explained to me (after much head-scratching) goes:
g :: y -> (w, z)
g = undefined
f :: x -> y
f = undefined
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge g f v = fst (g (f v))
I'm getting hung up on this example in two ways.
First, it seems like the munge function ought to take a function as input which takes x -> y. But the way munge is defined, it seems like we supply an additional argument v to the function f first. But if f :: x -> y, then won't the expression f v be of type just y instead of x -> y?
Second, I'm struggling to understand why the x appears in the second-to-last position in the type declaration. At that point I feel like the logical next piece after the (y -> (w,x)) step should just be w, since at that stage the function g is being applied to fst and w ought to be the type of what fst returns. I can feel that I'm close, but can't quite close the gap.
Clearly I'm not understanding the notation correctly. Can anyone set me straight?
EDIT: Ok, here is a clarifying question to the second part. Is it possible to revise the munge function so that it has the following type (i.e. original type with second-to-last x application omitted)? If so what would it look like?
munge :: (x -> y) -> (y -> (w, z)) -> w
The answer is incorrect and ill-typed. f and g should be swapped:
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge g f v = fst (f (g v))
I'm not sure if that clears up your confusion.
EDIT In case it's interesting, here are more equivalent ways of writing this function and its type:
-- notice parens in type signature; `->` associates right
munge :: (x -> y) -> ((y -> (w, z)) -> (x -> w))
munge g f v = -- omitted
-- type signature omitted
munge boop _plort zOWY = fst (_plort (boop zOWY))
munge g f = fst . f . g
munge g = \f v -> fst . f . g $ v
-- don't do this please
munge = ((fst .) .) . (.)
EDIT2 It might be helpful to play around with this in GHCi, asking the inferred type of different expressions:
Prelude> let munge g f v = fst (f (g v))
Prelude> :t munge
munge :: (t1 -> t) -> (t -> (a, b)) -> t1 -> a
Prelude> :t munge head
munge head :: (t -> (a, b)) -> [t] -> a
Prelude> :t munge head (\x-> (x, not x))
munge head (\x-> (x, not x)) :: [Bool] -> Bool
Prelude> :t munge ((+1) . fst . snd . head)
munge ((+1) . fst . snd . head)
:: Num t => (t -> (a, b)) -> [(a1, (t, b1))] -> a
The solution, confusingly, is using the same variables f and g for two different things: as global names for two functions, and as parameter names in defining munge. Making a change of variable should make it clearer:
g :: y -> (w, z)
g = undefined
f :: x -> y
f = undefined
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge f1 f2 v = fst (f2 (f1 v)) -- fst . f2 . f1 $ v
Then you would call munge on f and g will thing like
munge f g someArgumentForF
Inside munge, f (called f1) is first applied to someArgumentForF (called v) to get a value that can be passed to g (called f2). This produces a tuple, and applying fst to the tuple returns the value of type w needed as the final result.
say ,
f :: a -> b
g :: b -> c
h :: c -> d
why the equation
h.(g.f) = (h.g).f
is right?
how to prove it?
and the composition operation is just a basic operation in Haskell,
or we can get one by ourselves? if so how to achieve it?
You can define the composition operator yourself as follows:
(.) :: (b -> c) -> (a -> b) -> a -> c
g . f = \x -> g (f x)
Now, to prove associativity:
lhs = h . (g . f)
= \x -> h ((g . f) x) -- substitution
= \x -> h ((\y -> g (f y)) x) -- substitution
= \x -> h (g (f x)) -- beta reduction
rhs = (h . g) . f
= \x -> (h . g) (f x) -- substitution
= \x -> (\y -> h (g y)) (f x) -- substitution
= \x -> h (g (f x)) -- beta reduction
Now, we have lhs = rhs. QED.
I believe I understand fmap . fmap for Functors, but on functions it's hurting my head for months now.
I've seen that you can just apply the definition of (.) to (.) . (.), but I've forgot how to do that.
When I try it myself it always turns out wrong:
(.) f g = \x -> f (g x)
(.) (.) (.) = \x -> (.) ((.) x)
\x f -> (.) ((.) x) f
\x f y -> (((.)(f y)) x)
\x f y g-> (((.)(f y) g) x)
\x f y g-> ((f (g y)) x)
\x f y g-> ((f (g y)) x):: t2 -> (t1 -> t2 -> t) -> t3 -> (t3 -> t1) -> t
If "just applying the definition" is the only way of doing it, how did anybody come up with (.) . (.)?
There must be some deeper understanding or intuition I'm missing.
Coming up with (.) . (.) is actually pretty straightforward, it's the intuition behind what it does that is quite tricky to understand.
(.) gets you very far when rewriting expression into the "pipe" style computations (think of | in shell). However, it becomes awkward to use once you try to compose a function that takes multiple arguments with a function that only takes one. As an example, let's have a definition of concatMap:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f xs = concat (map f xs)
Getting rid of xs is just a standard operation:
concatMap f = concat . map f
However, there's no "nice" way of getting rid of f. This is caused by the fact, that map takes two arguments and we'd like to apply concat on its final result.
You can of course apply some pointfree tricks and get away with just (.):
concatMap f = (.) concat (map f)
concatMap f = (.) concat . map $ f
concatMap = (.) concat . map
concatMap = (concat .) . map
But alas, readability of this code is mostly gone. Instead, we introduce a new combinator, that does exactly what we need: apply the second function to the final result of first one.
-- .: is fairly standard name for this combinator
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x y = f (g x y)
concatMap = concat .: map
Fine, that's it for motivation. Let's get to the pointfree business.
(.:) = \f g x y -> f (g x y)
= \f g x y -> f ((g x) y)
= \f g x y -> f . g x $ y
= \f g x -> f . g x
Now, here comes the interesting part. This is yet another of the pointfree tricks that usually helps when you get stuck: we rewrite . into its prefix form and try to continue from there.
= \f g x -> (.) f (g x)
= \f g x -> (.) f . g $ x
= \f g -> (.) f . g
= \f g -> (.) ((.) f) g
= \f -> (.) ((.) f)
= \f -> (.) . (.) $ f
= (.) . (.)
As for intuition, there's this very nice article that you should read. I'll paraphrase the part about (.):
Let's think again about what our combinator should do: it should apply f to the result of result of g (I've been using final result in the part before on purpose, it's really what you get when you fully apply - modulo unifying type variables with another function type - the g function, result here is just application g x for some x).
What it means for us to apply f to the result of g? Well, once we apply g to some value, we'll take the result and apply f to it. Sounds familiar: that's what (.) does.
result :: (b -> c) -> ((a -> b) -> (a -> c))
result = (.)
Now, it turns out that composition (our of word) of those combinators is just a function composition, that is:
(.:) = result . result -- the result of result
You can also use your understanding of fmap . fmap.
If you have two Functors foo and bar, then
fmap . fmap :: (a -> b) -> foo (bar a) -> foo (bar b)
fmap . fmap takes a function and produces an induced function for the composition of the two Functors.
Now, for any type t, (->) t is a Functor, and the fmap for that Functor is (.).
So (.) . (.) is fmap . fmap for the case where the two Functors are (->) s and (->) t, and thus
(.) . (.) :: (a -> b) -> ((->) s) ((->) t a) -> ((->) s) ((->) t b)
= (a -> b) -> (s -> (t -> a)) -> (s -> (t -> b))
= (a -> b) -> (s -> t -> a ) -> (s -> t -> b )
it "composes" a function f :: a -> b with a function of two arguments, g :: s -> t -> a,
((.) . (.)) f g = \x y -> f (g x y)
That view also makes it clear that, and how, the pattern extends to functions taking more arguments,
(.) :: (a -> b) -> (s -> a) -> (s -> b)
(.) . (.) :: (a -> b) -> (s -> t -> a) -> (s -> t -> b)
(.) . (.) . (.) :: (a -> b) -> (s -> t -> u -> a) -> (s -> t -> u -> b)
etc.
Your solution diverges when you introduce y. It should be
\x f y -> ((.) ((.) x) f) y :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> ((.) ((.) x) f) y z :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> ((.) x (f y)) z :: (c -> d) -> (a -> b -> c) -> a -> b -> d
-- Or alternately:
\x f y z -> (x . f y) z :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> (x (f y z)) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
Which matches the original type signature: (.) . (.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(It's easiest to do the expansion in ghci, where you can check each step with :t expression)
Edit:
The deeper intution is this:
(.) is simply defined as
\f g -> \x -> f (g x)
Which we can simplify to
\f g x -> f (g x)
So when you supply it two arguments, it's curried and still needs another argument to resolve.
Each time you use (.) with 2 arguments, you create a "need" for one more argument.
(.) . (.) is of course just (.) (.) (.), so let's expand it:
(\f0 g0 x0 -> f0 (g0 x0)) (\f1 g1 x1 -> f1 (g1 x1)) (\f2 g2 x2 -> f2 (g2 x2))
We can beta-reduce on f0 and g0 (but we don't have an x0!):
\x0 -> (\f1 g1 x1 -> f1 (g1 x1)) ((\f2 g2 x2 -> f2 (g2 x2)) x0)
Substitute the 2nd expression for f1...
\x0 -> \g1 x1 -> ((\f2 g2 x2 -> f2 (g2 x2)) x0) (g1 x1)
Now it "flips back"! (beta-reduction on f2):
This is the interesting step - x0 is substituted for f2 -- This means that x, which could have been data, is instead a function.
This is what (.) . (.) provides -- the "need" for the extra argument.
\x0 -> \g1 x1 -> (\g2 x2 -> x0 (g2 x2)) (g1 x1)
This is starting to look normal...
Let's beta-reduce a last time (on g2):
\x0 -> \g1 x1 -> (\x2 -> x0 ((g1 x1) x2))
So we're left with simply
\x0 g1 x1 x2 -> x0 ((g1 x1) x2)
, where the arguments are nicely still in order.
So, this is what I get when I do a slightly more incremental expansion
(.) f g = \x -> f (g x)
(.) . g = \x -> (.) (g x)
= \x -> \y -> (.) (g x) y
= \x -> \y -> \z -> (g x) (y z)
= \x y z -> (g x) (y z)
(.) . (.) = \x y z -> ((.) x) (y z)
= \x y z -> \k -> x (y z k)
= \x y z k -> x (y z k)
Which, according to ghci has the correct type
Prelude> :t (.) . (.)
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
Prelude> :t \x y z k -> x (y z k)
\x y z k -> x (y z k)
:: (t1 -> t) -> (t2 -> t3 -> t1) -> t2 -> t3 -> t
Prelude>
While I don't know the origins of this combinator, it is likely that it was
developed for use in combinatory logic, where you work strictly with combinators,
so you can't define things using more convenient lambda expressions. There may be
some intuition that goes with figuring these things out, but I haven't found it.
Most likely, you would develop some level of intuition if you had to do it enough.
It's easiest to write equations, combinators-style, instead of lambda-expressions: a b c = (\x -> ... body ...) is equivalent to a b c x = ... body ..., and vice versa, provided that x does not appear among {a,b,c}. So,
-- _B = (.)
_B f g x = f (g x)
_B _B _B f g x y = _B (_B f) g x y
= (_B f) (g x) y
= _B f (g x) y
= f ((g x) y)
= f (g x y)
You discover this if, given f (g x y), you want to convert it into a combinatory form (get rid of all the parentheses and variable repetitions). Then you apply patterns corresponding to the combinators' definitions, hopefully tracing this derivation backwards. This is much less mechanical/automatic though.
Could you please explain the meaning of the expression ((.).(.))?
As far as I know (.) has the type (b -> c) -> (a -> b) -> a -> c.
(.) . (.) is the composition of the composition operator with itself.
If we look at
((.) . (.)) f g x
we can evaluate that a few steps, first we parenthesise,
((((.) . (.)) f) g) x
then we apply, using (foo . bar) arg = foo (bar arg):
~> (((.) ((.) f)) g) x
~> (((.) f) . g) x
~> ((.) f) (g x)
~> f . g x
More principled,
(.) :: (b -> c) -> (a -> b) -> (a -> c)
So, using (.) as the first argument of (.), we must unify
b -> c
with
(v -> w) -> (u -> v) -> (u -> w)
That yields
b = v -> w
c = (u -> v) -> (u -> w)
and
(.) (.) = ((.) .) :: (a -> v -> w) -> a -> (u -> v) -> (u -> w)
Now, to apply that to (.), we must unify the type
a -> v -> w
with the type of (.), after renaming
(s -> t) -> (r -> s) -> (r -> t)
which yields
a = s -> t
v = r -> s
w = r -> t
and thus
(.) . (.) :: (s -> t) -> (u -> r -> s) -> (u -> r -> t)
and from the type we can (almost) read that (.) . (.) applies a function (of one argument) to the result of a function of two arguments.
You've got an answer already, here's a slightly different take on it.
In combinatory logic (.) is B-combinator : Babc = a(bc). When writing combinator expressions it is customary to assume that every identifier consists of one letter only, and omit white-space in application, to make the expressions more readable. Of course the usual currying applies: abcde is (((ab)c)d)e and vice versa.
(.) is B, so ((.) . (.)) == (.) (.) (.) == BBB. So,
BBBfgxy = B(Bf)gxy = (Bf)(gx)y = Bf(gx)y = (f . g x) y
abc a bc a b c
We can throw away both ys at the end (this is known as eta-reduction: Gy=Hy --> G=H, if y does not appear inside H1). But also, another way to present this, is
BBBfgxy = B(Bf)gxy = ((f .) . g) x y = f (g x y) -- (.) f == (f .)
-- compare with: (f .) g x = f (g x)
((f .) . g) x y might be easier to type in than ((.).(.)) f g x y, but YMMV.
1 For example, with S combinator, defined as Sfgx = fx(gx), without regard for that rule we could write
Sfgx = fx(gx) = B(fx)gx = (f x . g) x
Sfg = B(fx)g = (f x . g) --- WRONG, what is "x"?
which is nonsense.
I am working through the 20 Intermediate Haskell Exercises at the moment, which is quite a fun exercise. It involves implementing various instances of the typeclasses Functor and Monad (and functions that takes Functors and Monads as arguments) but with cute names like Furry and Misty to disguise what we're doing (makes for some interesting code).
I've been trying to do some of this in a point-free style, and I wondered if there's a general scheme for turning a point-ful (?) definition into a point-free definition. For example, here is the typeclass for Misty:
class Misty m where
unicorn :: a -> m a
banana :: (a -> m b) -> m a -> m b
(the functions unicorn and banana are return and >>=, in case it's not obvious) and here's my implementation of apple (equivalent to flip ap):
apple :: (Misty m) => m a -> m (a -> b) -> m b
apple x f = banana (\g -> banana (unicorn . g) x) f
Later parts of the exercises have you implement versions of liftM, liftM2 etc. Here are my solutions:
appleTurnover :: (Misty m) => m (a -> b) -> m a -> m b
appleTurnover = flip apple
banana1 :: (Misty m) => (a -> b) -> m a -> m b
banana1 = appleTurnover . unicorn
banana2 :: (Misty m) => (a -> b -> c) -> m a -> m b -> m c
banana2 f = appleTurnover . banana1 f
banana3 :: (Misty m) => (a -> b -> c -> d) -> m a -> m b -> m c -> m d
banana3 f x = appleTurnover . banana2 f x
banana4 :: (Misty m) => (a -> b -> c -> d -> e) -> m a -> m b -> m c -> m d -> m e
banana4 f x y = appleTurnover . banana3 f x y
Now, banana1 (equivalent to liftM or fmap) I was able to implement in pointfree style, by a suitable definition of appleTurnover. But with the other three functions I've had to use parameters.
My question is: is there a recipe for turning definitions like these into point-free definitions?
As demonstrated by the pointfree utility, it's possible to do any such conversion automatically. However, the result is more often obfuscated than improved. If one's goal is to enhance legibility rather than destroy it, then the first goal should be to identify why an expression has a particular structure, find a suitable abstraction, and build things up that way.
The simplest structure is simply chaining things together in a linear pipeline, which is plain function composition. This gets us pretty far just on its own, but as you noticed it doesn't handle everything.
One generalization is to functions with additional arguments, which can be built up incrementally. Here's one example: Define onResult = (. (.)). Now, applying onResult n times to an initial value of id gives you function composition with the result of an n-ary function. So we can define comp2 = onResult (.), and then write comp2 not (&&) to define a NAND operation.
Another generalization--which encompasses the above, really--is to define operators that apply a function to a component of a larger value. An example here would be first and second in Control.Arrow, which work on 2-tuples. Conal Elliott's Semantic Editor Combinators are based on this approach.
A slightly different case is when you have a multi-argument function on some type b, and a function a -> b, and need to combine them into a multi-argument function using a. For the common case of 2-ary functions, the module Data.Function provides the on combinator, which you can use to write expressions like compare `on` fst to compare 2-tuples on their first elements.
It's a trickier issue when a single argument is used more than once, but there are meaningful recurring patterns here that can also be extracted. A common case here is applying multiple functions to a single argument, then collecting the results with another function. This happens to correspond to the Applicative instance for functions, which lets us write expressions like (&&) <$> (> 3) <*> (< 9) to check if a number falls in a given range.
The important thing, if you want to use any of this in actual code, is to think about what the expression means and how that's reflected in the structure. If you do that, and then refactor it into pointfree style using meaningful combinators, you'll often make the intent of the code clearer than it would otherwise be, unlike the typical output of pointfree.
Yes! One of the tricks is to write your dots in prefix notation rather than infix. Then you should be able to find new things that look like function composition. Here's an example:
banana2 f = appleTurnover . banana1 f
= (.) appleTurnover (banana1 f)
= ((.) appleTurnOver) . banana1 $ f
banana2 = (appleTurnover .) . banana1
The source code for the pointfree utility contains more, but this one handles a lot of cases.
banana4 f x y = appleTurnover . banana3 f x y
= (.) appleTurnover ((banana3 f x) y)
= ((.) appleTurnover) . (banana3 f x) $ y
banana4 f x = ((.) appleTurnover) . (banana3 f x)
= (.) ((.) appleTurnover) (banana3 f x)
= ((.) ((.) appleTurnover)) ((banana3 f) x)
= ((.) ((.) appleTurnover)) . (banana3 f) $ x
banana4 f = ((.) ((.) appleTurnover)) . (banana3 f)
= (.) ((.) ((.) appleTurnover)) (banana3 f)
= ((.) ((.) ((.) appleTurnover))) (banana3 f)
= ((.) ((.) ((.) appleTurnover))) . banana3 $ f
banana4 = ((.) ((.) ((.) appleTurnover))) . banana3
= (((appleTurnover .) .) .) . banana3
I use the following term rewrite system:
\x -> f x ------> f
f y x ----------> flip f x y
\x -> f (g x) --> f . g
It is incomplete (read why in books about combinatory logic), but it's enough:
Here is banana2:
banana2 f = appleTurnover . banana1 f
Rewrite as a lambda:
banana2 = \f -> appleTurnover . banana1 f
Write (.) in prefix style:
banana2 = \f -> (.) appleTurnover (banana1 f)
Note that
banana2 = \f -> ((.) appleTurnover) (banana1 f)
So rule 3 can be applied. f is (.) appleTurnover and g is banana:
banana2 = ((.) appleTurnover) . banana1
There is a pointfree package which takes a Haskell function definition and attempts to re-write it in a pointfree style. I'd suggest experimenting with it to get new ideas. See this page for more details; the package is available here.
Since pointfree style is combinators style, just apply known combinators definitions, reading them backwards to make the substitution:
B f g x = f (g x) -- (.) , <$> for ((->) a)
C f x y = f y x -- flip
K x y = x -- const
I x = x -- id
S f g x = f x (g x) -- <*> , ap for ((->) a)
W f x = f x x -- join
(f >>= g) x = g (f x) x
(f =<< g) x = f (g x) x
At times liftMx, liftAx, sequence, sequenceA can simplify things. I'd also consider foldr, unfoldr, iterate, until etc. as basic combinators.
Often, using operator sections helps too:
op a b = (a `op` b) = (`op` b) a = (a `op`) b
Some patterns can become familiar and so, used directly:
((f .) . g) x y = f (g x y)
((. f) . g) x y = g x (f y)
(((f .) .) . g) x y z = (f .) (g x y) z = f (g x y z)
(((. f) .) . g) x y z = (. f) (g x y) z = g x y (f z)
etc.