推断这个免费代码的步骤是什么?

ice*_*man 6 haskell pointfree lifting

我正在审查一些代码,并遇到了以下gem,我下注的是pointfree输出的复制粘贴:

(我认为以下内容比通常foo/ bar对于这个特定问题更合适:P)

import Control.Monad (liftM2)

data Battleship = Battleship { x :: Int
                             , y :: Int
                             } deriving Show

placeBattleship :: Int -> Int -> Battleship
placeBattleship x' y' = Battleship { x = x', y = y' }

coordinates :: Battleship -> (Int, Int)
coordinates = liftM2 (,) x y
Run Code Online (Sandbox Code Playgroud)

有人会善意地解释简化所需的步骤:

(i)coordinates b = (x b, y b)

到:

(ii)coordinates = liftM2 (,) x y

特别是,我对使用有点困惑,liftM2因为我甚至不知道monad潜伏在后台.

我知道(i)也可以表示为:coordinates s = (,) (x s) (y s)但我不知道在哪里/如何继续.


PS以下是我怀疑它来自pointfree(输出来自GHCI:pl别名pointfree)的原因:

?: :pl coordinates s = (x s, y s)
coordinates = liftM2 (,) x y
Run Code Online (Sandbox Code Playgroud)

Tik*_*vis 9

这利用了Monad实例(->) r,也称为"读者monad".这是特定类型到函数的monad a.(看看这里为什么它存在于第一位的动机.)

要了解它如何适用于各种功能,请m使用(r ->in 替换m a.例如,如果我们这样做liftM,我们得到:

liftM :: (a -> b) -> (m a -> m b)
liftM :: (a -> b) -> ((r -> a) -> (r -> b))
      :: (a -> b) -> (r -> a) -> (r -> b) -- simplify parentheses
Run Code Online (Sandbox Code Playgroud)

......这只是功能构成.整齐.

我们可以做同样的事情liftM2:

liftM2 :: (a -> b -> c) -> m a -> m b -> m c
liftM2 :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c)
Run Code Online (Sandbox Code Playgroud)

所以我们看到的是用两个参数函数组合两个单参数函数的方法.这是将正常函数组合推广到多个参数的一种方法.我们的想法是创建一个函数,r通过传递两个单参数函数来获取单个函数,将两个参数传递给双参数函数.所以如果我们有f :: (r -> a),g :: (r -> b)并且h :: (a -> b -> c)我们生产:

\ r -> h (f r) (h r)
Run Code Online (Sandbox Code Playgroud)

现在,这如何适用于您的代码?(,)是两个参数的功能,x以及y是类型的一个参数的功能Battleship -> Int(因为这是场存取的工作方式).考虑到这一点:

liftM2 (,) x y = \ r -> (,) (x r) (y r)
               = \ r -> (x r, y r)
Run Code Online (Sandbox Code Playgroud)

一旦你内化了像这样的多功能组合的想法,像这样的无点代码变得更加可读 - 不需要使用pointfree工具!在这种情况下,我认为非无版本的版本仍然更好,但是无点版本本身并不可怕.


bhe*_*ilr 5

monad liftM2在这里工作的是monad函数(->) a.这相当于Readermonad,正如您之前所见.

回想一下定义liftM2:

liftM2 :: Monad m => (a -> b -> r) -> m a -> m b -> m r
liftM2 f ma mb = do
    a <- ma
    b <- mb
    return $ f a b
Run Code Online (Sandbox Code Playgroud)

所以现在,如果我们替换(,)为for f,xfor mayfor mb,我们得到

liftM2 (,) x y = do
    a <- x
    b <- y
    return $ (,) a b
Run Code Online (Sandbox Code Playgroud)

由于x, y :: Battleship -> Int这相当于((->) Battleship) Int,然后m ~ (->) Battleship.函数monad定义为

instance Monad ((->) a) where
    return x = const x
    m >>= f = \a -> f (m a) a
Run Code Online (Sandbox Code Playgroud)

本质上monad所做的功能是允许你从几个函数中提取输出,只要它们都具有相同的输入.一个更清晰的例子可能是这样的

test = do
    a <- (^2)
    b <- (^3)
    c <- (^4)
    d <- show
    return (a, b, c, d)

> test 2
(4, 8, 16, "2")
Run Code Online (Sandbox Code Playgroud)