【问题标题】:What are the steps for deducing this pointfree code?推导出这个无点代码的步骤是什么?
【发布时间】:2015-01-17 16:30:36
【问题描述】:

我在查看一些代码时遇到了以下 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

有人能解释一下从:

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

到:

(ii) coordinates = liftM2 (,) x y 所需的步骤吗?

特别是,我对 liftM2 的使用感到有些困惑,因为我什至不知道背后潜伏着一个 monad。

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


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

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

【问题讨论】:

    标签: haskell pointfree lifting


    【解决方案1】:

    这利用了(->) rMonad 实例,也称为“reader monad”。这是特定类型到a 的函数的单子。 (看看here,了解它为什么存在的动机。)

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

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

    \ r -> h (f r) (h r)
    

    现在,这如何应用于您的代码? (,) 是双参数函数,xyBattleship -> Int 类型的单参数函数(因为这就是字段访问器的工作方式)。考虑到这一点:

    liftM2 (,) x y = \ r -> (,) (x r) (y r)
                   = \ r -> (x r, y r)
    

    一旦你内化了像这样的多函数组合的想法,像这样的无点代码变得更具可读性——无需使用无点工具!在这种情况下,我认为non-pointfree的版本还是更好,但是pointfree的本身并不可怕。

    【讨论】:

      【解决方案2】:

      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 -&gt; Int 相当于((-&gt;) Battleship) Int,所以m ~ (-&gt;) 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")
      

      【讨论】:

        【解决方案3】:

        你可以很容易地重写

        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)
        

        这不是无点风格,但很简单

        【讨论】:

          猜你喜欢
          • 2010-10-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多