【发布时间】:2012-02-06 23:45:26
【问题描述】:
我正在学习 Haskell。很抱歉问了一个非常基本的问题,但我似乎找不到答案。我有一个由 f 定义的函数:
f x = g x x
其中 g 是已定义的 2 个参数的函数。我如何编写这种无点风格? 编辑:不使用 lambda 表达式。
谢谢
【问题讨论】:
我正在学习 Haskell。很抱歉问了一个非常基本的问题,但我似乎找不到答案。我有一个由 f 定义的函数:
f x = g x x
其中 g 是已定义的 2 个参数的函数。我如何编写这种无点风格? 编辑:不使用 lambda 表达式。
谢谢
【问题讨论】:
f可以写成Control.Monad.join:
f = join g
函数 monad 上的join 是构造无点表达式时使用的原语之一,因为它本身不能以无点样式定义(其SKI calculus 等效,.SII — @ Haskell 中的 987654330@ - 不输入)
【讨论】:
f = join g
join 函数是 monad 的基本函数,在这种情况下,monad 是函数 monad,(->) r 是一些 r。 join :: Monad m => m (m a) -> m a的定义;用(->) r 代替m 得到join :: (r -> r -> a) -> (r -> a)。
join 是无点风格的原语,因为 SII 到 Haskell 的翻译 ap id id 是无效的——它有一个类型错误(具体来说,它失败occurs check)。它在 SKI 组合子演算中有效,因为它是无类型的。
join 等于 flip ap id,因此可以说它不是原始的。请注意,虽然某些 SKI 表达式不键入,但它们通常不是唯一的,有时替代方案会更好。例如,I = SKK = SKS,但在这两个中只有 ap const const 具有您想要的类型。
这被称为"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
-}
【讨论】:
ι (iota) 组合器 - λf.fSK、SKI 然后表示为 S = ι(ι(ι(ιι)))、K = ι(ι(ιι)) 和 I = ιι。这个基础对于简单类型的 λ 演算不是那么友好,ιι 甚至不进行类型检查。 U = λf.fKSK 好一点(不知道有没有名字,所以我就叫它U 通用); S = U(UU) 和 K = UUU
f = g =<< id = id =<< g。
我来到这里纯属偶然,我想提供我的解决方案,因为在这个帖子中还没有人提到提升,至少没有明确地提到。
这是一个解决方案:
f = liftM2 g id id
怎么看?
g 具有 a -> a -> b 类型,即它需要 某种类型的两个值(两者的类型相同,否则 OP 给出的 f 的定义不会有意义),并返回另一个 某种类型的值(不一定与参数的类型相同);
lift2M g 是g 的提升版本,它的类型为(Monad m) => m a -> m a -> m b:它采用两个一元值,每个值都是迄今为止未指定的值上下文,并返回一个一元值;
当将两个函数传递给liftM2 g 时,将在Monad 的上下文中投掷骰子:值尚未存在,但最终会存在,当函数将接收它需要的参数;换句话说,函数是存储自己未来值的单子;因此,lift2M g 接受两个函数的输入(或者,两个函数的未来值),并返回另一个函数(或者,它的未来值);知道了这个,把m改成(->) r,或者r ->:(r -> a) -> (r -> a) -> (r -> b)
我们传递的两个函数都是id,它保证它会返回它收到的相同值;
因此liftM2 g id id 是一个r -> b 类型的函数,它将其参数传递给这两个ids,使其保持不变并将其转发给g。
以类似的方式,可以利用函数是应用函子,并使用以下解决方案:
f = g <$> id <*> id
【讨论】:
liftA2,对于函数(即Reader monad)与liftM2相同:liftA2 k f g x = k (f x) (g x)。 (这个就在:liftA2 (,) === (&&&) :))。
g <$> x <*> y === liftA2 g x y,或多或少定义为 IIRC)