Pointfree returning a tuple in Haskell
문제
Can a pointfree function return a tuple? For instance, can the following be written in pointfree style (where f1, f2, and f3 have been defined):
(\t -> (f1 t, f2 t, f3 t))
In this case, my f1, f2, and f3 are compositions of quot, mod, *, and some integers.
(\f1,f2,f3 -> (\t -> (f1 t, f2 t, f3 t)))
is a more general case, and is equivalent to
(\f1,f2,f3,t -> (f1 t, f2 t, f3 t))
Named functions are OK, but my examples are anonymous. (Named examples would be as follows)
f x = (f1 x, f2 x, f3 x)
f f1 f2 f3 x = (f1 x, f2 x, f3 x)
EDIT: I'm just curious for fun, I'm not going to do this.
해결책
You can write
(\t -> (f1 t, f2 t, f3 t))
pointfree, it's
liftM (,,) f1 `ap` f2 `ap` f3
with ap
from Control.Monad
and the Monad
instance of (->) a
from Control.Monad.Instances
. A somewhat more readable form may be the Control.Applicative
variant
(,,) <$> f1 <*> f2 <*> f3
You can then further point-free
(\f1 f2 f3 -> (\t -> (f1 t, f2 t, f3 t)))
As
\f1 f2 f3 -> (,,) <$> f1 <*> f2 <*> f3
= \f1 f2 -> ((,,) <$> f1 <*> f2 <*>)
= \f1 f2 -> (<*>) ((,,) <$> f1 <*> f2)
= \f1 f2 -> ((<*>) . ((,,) <$> f1 <*>)) f2
= \f1 -> (<*>) . ((,,) <$> f1 <*>)
= \f1 -> (<*>) . (<*>) ((,,) <$> f1)
= \f1 -> (((<*>) .) . (<*>) . (<$>) (,,)) f1
= ((<*>) .) . (<*>) . (<$>) (,,)
but seriously, you shouldn't. Keep it readable, that means a bit of pointfreeing is good, but don't overdo it.
다른 팁
Yes. The "lambdabot" IRC bot on the #haskell IRC channel actually has a feature which gives you the point-free version of a given function. In your case, it says that
\x -> (f x, g x, h x)
is equivalent to
ap (liftM2 (,,) f g) h
Although the applicative or monadic version is simpler and shorter, one way that perhaps exposes the "meaning" (and what property of the category of Haskell types you are using) is using Control.Arrow
uncurry (uncurry (,,)) . ((f &&& g) &&& h)
The pointfull version is superior though.
This also exposes that you need the "Cartesianess" of Hask, but not all the "Closedness" of Hask
arrowized :: Arrow cat => cat a a1 -> cat a b1 -> cat a b -> cat a (a1, b1, b)
arrowized f g h => arr (uncurry (uncurry (,,))) . ((f &&& g) &&& h)
You can write your example like this:
\f1 f2 f3 t -> (,,) (f1 t) (f2 t) (f3 t)
(,,) is a usual function with 3 arguments, so there's nothing special in making its application pointfree. However, it uses its argument 3 times so it's going to be cumbersome and it's probably not worth it.
Lambdabot at #haskell says it's (ap .) . liftM2 (,,)
. Enjoy :)
Here's some elaboration on the other's answers here.
Inside the source code for Control.Applicative
we find
instance Applicative ((->) a) where -- (a ->) is meant here
pure = const
(<*>) f g x = f x (g x)
liftA3 f a b c = f <$> a <*> b <*> c
In GHCi, we get
Prelude Control.Applicative> :t liftA3 (,,)
liftA3 (,,) :: (Applicative f) => f a -> f b -> f c -> f (a, b, c)
So, with (t->)
as f
, liftA3 (,,)
just works:
liftA3 (,,) ~ (t->a) -> (t->b) -> (t->c) -> (t->(a,b,c))
I.e., calling liftA3 (,,) f1 f2 f3 t
produces a triple (f1 t, f2 t, f3 t)
, given three functions on same-type input:
Prelude Control.Applicative>
liftA3 (,,) (:[]) (quot 12) (`rem`3) 4
([4],3,1)
So, how does it work? By the definiton of liftA3
, and then of <*>
,
liftA3 (,,) f g h t = ((((,,) <$> f) <*> g) <*> h) t
= (((,,) <$> f) <*> g) t (h t)
= (((,,) <$> f) t (g t) (h t)
Now, (<$>) = fmap
and instance Functor ((->) t)
defines fmap = (.)
, so we continue
= (((,,) . f) t (g t) (h t)
= (,,) (f t) (g t) (h t)
= (f t, g t, h t)