以点无形式编写代码:f x = g x x。

17

我正在学习Haskell。非常抱歉问一个非常基础的问题,但我似乎找不到答案。我有一个由以下方式定义的函数f:

f x = g x x

如何以点式风格编写已定义了两个参数的函数g?

编辑: 不使用lambda表达式。

谢谢。

3个回答

21

f 可以用 Control.Monad.join 来表示:

f = join g

join函数单子是构造无输入表达式时使用的原语之一,因为它本身无法以无输入方式定义(它在SKI演算中等价于SII——在Haskell中是ap id id——但其类型不适用)


2
我认为你的意思是 f = join g - dflemstr
1
@user1188374: join是无点风格的原语,因为将SII翻译成Haskell时,ap id id是无效的——它有一个类型错误(具体来说,它未通过出现检查)。在SKI组合子演算中,它可以工作,因为它是无类型的。 - ehird
2
“join” 等于 “flip ap id”,因此可以认为它不是原语。请注意,虽然一些 SKI 表达式无法类型化,但它们通常不是唯一的,有时候另一种替代方法会更好。例如,“I = SKK = SKS”,但其中只有 “ap const const” 具有所需的类型。 - Ben Millwood
1
@dflemstr:在Haskell /库中,函数单子实例实际上定义在哪里? 在ghci中,Control.Monad.join (+)会出现错误:No instance for (Monad ((->) a0)) arising from a use of join' Possible fix: add an instance declaration for (Monad ((->) a0))。(我看到Control.Monad haddock有instance Monad ((->) r)`,但没有相应的源代码(因为它在GHC中是原始的?)并且对我来说还有一步缺失。) - misterbee
2
@misterbee 它被声明在 Control.Monad.Instances 中。 - dflemstr
显示剩余9条评论

9
这被称为"W combinator"
import Control.Monad
import Control.Monad.Instances
import Control.Applicative

f = join g       -- = Wg        (also, join = (id =<<))
  = (g `ap` id)  -- \x -> g x (id x) = SgI
  = (<*> id) g   --                  = CSIg
  = g =<< id     -- \x -> g (id x) x
  = id =<< g     -- \x -> id (g x) x

S,K,I是一个基本的组合子集合;B,C,K,W则是另一个——你必须在某个地方停下来(关于你的“无lambda表达式”评论)

_B = (.)     -- _B f g x = f (g x)     = S(KS)K
_C = flip    -- _C f x y = f y x       = S(S(K(S(KS)K))S)(KK)
_K = const   -- _K x y   = x
_W = join    -- _W f x   = f x x       = CSI = SS(KI) = SS(SK)
_S = ap      -- _S f g x = f x (g x)   = B(B(BW)C)(BB) = B(BW)(BBC)
   = (<*>)                                -- from Control.Applicative
_I = id      -- _I x     = x           = WK = SKK = SKS = SK(...)

{-
Wgx = gxx 
    = SgIx = CSIgx 
           = Sg(KIg)x = SS(KI)gx
    = gx(Kx(gx)) = gx(SKgx) = Sg(SKg)x = SS(SK)gx

-- _W (,) 5 = (5,5)
-- _S _I _I x = x x = _omega x         -- self-application, untypeable
-}

2
停止点是一个组合子。一个例子是 ι(iota)组合子 - λf.fSK,然后 SKI 表示为 S = ι(ι(ι(ιι)))K = ι(ι(ιι))I = ιι。这个基础对于简单类型的 λ-演算来说并不友好,ιι 甚至无法通过类型检查。U = λf.fKSK 更好一些(我不知道它是否有名称,所以我只是称之为通用的 U);S = U(UU)K = UUU - Vitus
@Vitus 是的,是的。我想这更多是一个方便的问题,在实际环境中该停止哪里。例如在Haskell中,我们有SKI BCKW组合子是很方便的。 - Will Ness
1
同时,f = g =<< id = id =<< g - Will Ness

1
我是一位有用的助手,可以为您翻译文本。以下是需要翻译的内容:

我是偶然来到这里的,我想提供我的解决方案,因为在这个帖子中还没有人明确提到举升。

这是一个解决方案:

f = liftM2 g id id

如何看待它?
  • g 的类型为 a -> a -> b,即它接受 某种类型的两个值(必须是相同类型,否则 OP 给出的 f 的定义就没有意义),并返回另一个某种类型的值(不一定与参数类型相同);

  • lift2M gg 的提升版本,类型为 (Monad m) => m a -> m a -> m b:它接受 两个单子值,每个值都处于到目前为止未指定的上下文中,并返回一个单子值

  • 当将两个函数传递给 liftM2 g 时,掷骰子决定了 Monad 的上下文:这意味着值尚未存在其中,但在函数接收所需参数时将会存在;换句话说,函数是存储其自身未来值的单子;因此,lift2M g 接受两个函数(或两个函数的未来值),并返回另一个函数(或其未来值);知道这一点后,如果将 m 更改为 (->) rr ->,则其类型与上面相同:(r -> a) -> (r -> a) -> (r -> b)

  • 我们传递的两个函数都是 id,它保证会返回接收到的相同值;

  • 因此,liftM2 g id id 是一个类型为 r -> b 的函数,它将其参数传递给这两个 id,让其不变并将其转发给 g

以类似的方式,可以利用函数是应用函子的特性,使用以下解决方案:
f = g <$> id <*> id

或者 liftA2,对于函数来说与 liftM2 相同(即 Reader monad):liftA2 k f g x = k (f x) (g x)。(刚刚发布:liftA2 (,) === (&&&) :) )。 - Will Ness
嗨@WillNess,你的每一条评论都会在我的待学习清单上添加一个空复选框,哈哈。这种语言让我想回到大学。好吧,我甚至不知道在意大利哪里可以学习它 :/。我想我得去搜索一下。 - Enlico
1
先从Eugenio Moggi开始搜索吧。另外,顺便说一下,g <$> x <*> y === liftA2 g x y,根据我记得的定义差不多是这样的。 - Will Ness

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接