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

时间:2014-11-19 23:25:04

标签: haskell pointfree lifting

我正在审核一些代码,并遇到了以下宝石,我下注的是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

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

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

至:
(ii)coordinates = liftM2 (,) x y

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

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


P.S。以下是我怀疑它来自pointfree(输出来自GHCI:pl别名为pointfree)的原因:

λ: :pl coordinates s = (x s, y s)
coordinates = liftM2 (,) x y

3 个答案:

答案 0 :(得分:9)

这利用了Monad的{​​{1}}实例,也称为"读者monad"。这是特定类型到(->) r的函数monad。 (看一下here的动机,了解它为什么存在。)

要了解它如何适用于各种功能,请将a替换为m中的(r ->。例如,如果我们只做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 执行相同的操作:

liftM2

所以我们看到的是用双参数函数组合两个单参数函数的方法。它是将正常函数组合推广到多个参数的一种方法。我们的想法是创建一个函数,通过传递两个单参数函数来获取单个liftM2 :: (a -> b -> c) -> m a -> m b -> m c liftM2 :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c) ,将两个参数传递给双参数函数。因此,如果我们有rf :: (r -> a)g :: (r -> b),我们会生成:

h :: (a -> b -> c)

现在,这如何适用于您的代码? \ r -> h (f r) (h r) 是双参数函数,(,)xy类型的单参数函数(因为那是字段访问器的工作方式)。考虑到这一点:

Battleship -> Int

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

答案 1 :(得分:5)

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

回想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

现在,如果我们将(,)替换为f,将x替换为ma,将y替换为mb,我们就会

liftM2 (,) x y = do
    a <- x
    b <- y
    return $ (,) a b

由于x, y :: Battleship -> Int相当于((->) Battleship) Int,所以m ~ (->) Battleship。函数monad定义为

instance Monad ((->) a) where
    return x = const x
    m >>= f = \a -> f (m a) a

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

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

> test 2
(4, 8, 16, "2")

答案 2 :(得分:1)

你可以轻松地重写

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

placeBattleship :: Int -> Int -> Battleship
placeBattleship x y = Battleship x y

coordinates :: Battleship -> (Int, Int)
coordinates  (Battleship x y) = (x, y)

这不是无点风格,而是非常简单