【问题标题】:How can I make a combinator with this type signature?如何使用这种类型签名制作组合器?
【发布时间】:2018-08-07 22:23:11
【问题描述】:

我一直在尝试使用这种类型签名制作组合器:

(a -> b -> c) -> (c -> d -> e) -> a -> b -> d -> e

我浏览过Data.Aviary.Birds 和所有我能找到的默认编程帮助网站,但都无济于事。另外,如果有通用算法来制作这些,将不胜感激,但不是必需的。

【问题讨论】:

标签: haskell combinators tacit-programming


【解决方案1】:

我们的定义是这样开始的:

foo :: (a -> b -> c) -> (c -> d -> e) -> a -> b -> d -> e
foo abc cde a b d = e

现在让我们来填补缺失的部分。

我们需要一个e;获得它的唯一方法是将第二个函数应用于cd

e = cde c d

我们已经获得了d,但我们需要c。我们如何获得c?通过将第一个函数应用于ab

c = abc a b

我们得到了这两个,所以我们完成了。

foo :: (a -> b -> c) -> (c -> d -> e) -> a -> b -> d -> e
foo abc cde a b d = e
  where
    e = cde c d
    c = abc a b

我们可能会停在这里。这是一个非常好的定义。


但是如果我们想让它更简洁,让我们从替换e的定义开始

foo abc cde a b d = cde c d
  where
    c = abc a b

然后是c

foo abc cde a b d = cde (abc a b) d

我们立即看到我们可以通过 eta reduce 删除 d

foo abc cde a b = cde (abc a b)

该类型现在稍微更通用了。d -> e 已合并为一个类型变量,因为它实际上可以是任何东西。

foo :: (a -> b -> c) -> (c -> de) -> a -> b -> de

我们现在可以在 aviary 中看到我们的组合器实际上是翻转的黑鸟。

blackbird :: (c -> d) -> (a -> b -> c) -> a -> b -> d

foo :: (a -> b -> c) -> (c -> de) -> a -> b -> de
foo = flip blackbird

事实上,如果我们查看黑鸟的来源,它看起来很像我们所写的。

-- | B1 combinator - blackbird - specs 'oo'.
blackbird :: (c -> d) -> (a -> b -> c) -> a -> b -> d
blackbird f g x y = f (g x y)

我们可以更免点吗?我们可以考虑 uncurrying abc

foo abc cde a b = cde (uncurry abc (a, b))

用函数组合重写这个嵌套

foo abc cde a b = (cde . uncurry abc) (a, b)

然后又回来了

foo abc cde a b = curry (cde . uncurry abc) a b

现在我们可以再砍掉两个参数。

foo abc cde = curry (cde . uncurry abc)

我们绝对应该在这里停下来。但是如果我们现在把论点颠倒过来会怎样

foo = flip $ \cde abc -> curry (cde . uncurry abc)

重写右半部分使其无点

foo = flip $ \cde abc -> (curry . ((cde .) . uncurry)) abc

和 eta 再次减少

foo = flip $ \cde -> curry . ((cde .) . uncurry)

并采取最后荒谬的一步

foo = flip $ (curry .) . (. uncurry) . (.)

我们现在是免费的!

【讨论】:

  • flip ((.) . (.)) ?
  • flip ((.) . (.) . flip)。 (我最终选择了(f .) . g 以获得一点可读性。)感谢您的广泛回答!
  • @APerson,从实际的角度来看,(f .) . g 信息量很大。你可以写成rmap f . g,其中rmap来自Data.Profunctor(.)来自Control.Category。这更加概括了类型。您甚至可以使用Data.Semigroupoid 中的o 而不是(.) 来提高通用性。
  • 呃...我想我有点困惑。我不应该在困的时候写作。我早上会检查详细信息。
【解决方案2】:

有一个非常简单的方法:作弊。让我们从找出我们想要的功能开始。为此,我们转到Djinn。输入

f :: (a -> b -> c) -> (c -> d -> e) -> a -> b -> d -> e

它回来了

f a b c d = b (a c d)

很好。现在前往pointfree.io。粘贴来自 Djinn 的定义,它说

f = flip ((.) . (.))

完成。

【讨论】:

  • 谢谢!不知道那个工具!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-23
  • 2021-06-27
  • 1970-01-01
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多