以无点样式编写函数的一般方案是什么?

Chr*_*lor 8 haskell combinators pointfree higher-order-functions

我正在完成20个中级Haskell练习,这是一个非常有趣的练习.它涉及到执行类型类的各种实例FunctorMonad(而这需要职能FunctorS和MonadS作为参数),但像可爱的名字FurryMisty掩饰我们正在做什么(一些有趣的代码使).

我一直试图以无点的方式做一些这样的事情,我想知道是否有一个将点 - 完全(?)定义转变为无点定义的一般方案.例如,以下是类型类Misty:

class Misty m where
  unicorn :: a -> m a
  banana :: (a -> m b) -> m a -> m b
Run Code Online (Sandbox Code Playgroud)

(函数unicornbanana是,return并且>>=,如果不是很明显),这是我的apple(相当于flip ap)的实现:

apple :: (Misty m) => m a -> m (a -> b) -> m b
apple x f = banana (\g -> banana (unicorn . g) x) f
Run Code Online (Sandbox Code Playgroud)

演习后面的部分有你实现的版本liftM,liftM2等等.这里是我的解决方案:

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
Run Code Online (Sandbox Code Playgroud)

现在,banana1(相当于liftMfmap)我能够通过合适的定义以无点样式实现appleTurnover.但是使用其他三个函数我必须使用参数.

我的问题是:是否有将这些定义转化为无点定义的方法

C. *_*ann 11

如该pointfree实用程序所示,可以自动执行任何此类转换.但是,结果经常被混淆而不是改进.如果一个人的目标是提高可读性而不是破坏它,那么第一个目标应该是找出为什么一个表达式有一个特殊的结构,找到一个合适的抽象,并建立东西的方式.

最简单的结构就是在线性管道中将事物链接在一起,这是简单的功能组合.这让我们自己走得很远,但是你注意到它并没有处理所有事情.

一个概括是具有附加参数的函数,这些参数可以递增地构建.这是一个例子:定义onResult = (. (.)).现在,onResult对初始值应用n次id给出了具有n元函数结果的函数组合.因此我们可以定义comp2 = onResult (.),然后编写comp2 not (&&)以定义NAND操作.

另一个概括 - 实际上包含上述内容 - 是定义将函数应用于更大值的组件的运算符.这里有一个例子是firstsecondControl.Arrow,其中2元组工作.Conal Elliott的语义编辑器组合器基于这种方法.

稍有不同的情况是当你在某种类型b和函数上有一个多参数函数时a -> b,需要将它们组合成一个多参数函数a.对于2-ary函数的常见情况,该模块Data.Function提供了on组合器,您可以使用它来编写表达式,比如compare `on` fst在它们的第一个元素上比较2元组.

当一个参数被多次使用时,这是一个棘手的问题,但这里也有一些有意义的重复模式也可以被提取出来.这里的一个常见情况是将多个函数应用于单个参数,然后使用另一个函数收集结果.这恰好对应Applicative于函数的实例,这使我们可以编写表达式(&&) <$> (> 3) <*> (< 9)来检查数字是否落在给定范围内.

重要的是,如果你想在实际代码中使用任何一个,那就要考虑表达式的含义以及它在结构中的反映方式.如果你这样做,然后使用有意义的组合器将它重构为无点样式,你通常会使代码的意图比原本更清晰,不像典型的输出pointfree.


red*_*der 5

是! 其中一个技巧是用前缀表示法而不是中缀表示你的点.然后你应该能够找到看起来像功能组合的新东西.这是一个例子:

banana2 f = appleTurnover . banana1 f
          = (.) appleTurnover (banana1 f)
          = ((.) appleTurnOver) . banana1 $ f
banana2 = (appleTurnover .) . banana1
Run Code Online (Sandbox Code Playgroud)

pointfree实用程序的源代码包含更多内容,但是这个代码处理了很多情况.

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
Run Code Online (Sandbox Code Playgroud)

  • 这也是让你的功能完全不可读的好方法,当然...... (6认同)
  • 鉴于`return`被称为'独角兽',似乎OP并不担心那个= P. (4认同)