我有这个函数
rulesApply :: [PhrasePair] -> Phrase -> Phrase
rulesApply pp = try (transformationsApply "*" reflect pp )
我想学习如何让它毫无意义。
try :: (a -> Maybe a) -> a -> a
try f x = maybe x id (f x)
transformationsApply :: Eq a => a -> ([a] -> [a]) -> ([([a], [a])] -> ([a] -> Maybe [a]))
transformationsApply wc f pfPair list = foldr1 orElse (map (transformationApply wc f list) pfPair)
rulesApply pp = try (transformationsApply "*" reflect pp )
(transformationsApply "*" reflect ) pp
具有类型 Eq a => ([([a], [a])] -> ([a] -> Maybe [a]))
我们看到
try :: (a -> Maybe a) -> a -> a
所以 try 将函数(a -> Maybe a)
作为参数。 我们看到(transformationsApply "*" reflect ) pp
的返回类型是([a] -> Maybe [a]))
,所以我们应该能够编写。
rulesApply pp = try . (transformationsApply "*" reflect) pp
但这会产生编译错误。
每当你有看起来像的东西
x -> f (g x)
你可以把它变成
f . g
在这种情况下,您有
s x = f (g x )
rulesApply pp = try (transformationsApply "*" reflect pp )
可以(通过将参数移动到等式的另一侧)转换为
s = x -> f (g x )
rulesApply = pp -> try (transformationsApply "*" reflect pp )
反过来,根据我们的规则,
s = f . g
rulesApply = try . transformationsApply "*" reflect
删除点相对容易,但您应该逐步移动。
rulesApply pp = try ( transformationsApply "*" reflect pp)
=== [partial application]
rulesApply pp = try ((transformationsApply "*" reflect) pp)
=== [definition of (.)]
rulesApply pp = (try . transformationsApply "*" reflect) pp
=== [eta reduction]
rulesApply = try . transformationsApply "*" reflect
实际上非常简单:
rulesApply :: [PhrasePair] -> Phrase -> Phrase
rulesApply = try . transformationsApply "*" reflect
无点编程不仅仅是关于美学。这是关于在更高层次上处理问题:你不是对函数变量进行操作,而是对函数本身进行操作,从而消除了整个问题区域。
让我们分析一下(.)
运算符的签名。
(.) :: (b -> c) -> (a -> b) -> (a -> c)
我故意在a -> c
周围放大括号,以明确表示产生另一个函数需要两个函数。在这方面,它与基元值上的任何运算符没有太大区别,例如:
(+) :: Int -> Int -> Int
现在,不要沉迷于它,也不要指望它会适合你道路上的任何问题。这只是您口袋里的另一个工具,应该适当使用。最常见的用法是避免冗余的lambda。以下是一些示例:
putStrLn . (++ "!") == a -> putStrLn (a ++ "!")
void . return == a -> return a >> return ()
第二个例子基本上相当于const (return ()) == a -> return ()
,但出于美学原因,我更喜欢它。我认为,编译器无论如何都会优化这些东西。