这个函数可以写成无点风格吗?如果不是,为什么?
Can this function be written in point-free style? If not, why?
一个相关的问题是,但是一些答案说几乎任何东西都可以免费点,那么这个功能有什么问题吗?
\[x] -> x
http://pointfree.io/好像不能写成无点式的。这是否意味着它不能那样写?如果是这样,理论上的原因是什么?
我只能观察到上面的函数是 head
(或 last
,fwiw)的“残废”版本,它只能在单例列表上运行。事实上,应用于非单例列表,它在 ghci
:
中以这种方式出错
*** Exception: <interactive>:380:5-13: Non-exhaustive patterns in lambda
也许模式上的“非穷尽性”是某些函数不能用无点样式编写的原因?
根据答案编辑:
我没想到我的问题的答案会这么复杂(我觉得我只是认为简短的答案是不,它不能,实际上),所以我需要找时间仔细阅读它们,稍微试验一下,然后全神贯注,否则我无法决定应该接受哪一个。目前,对 Jon Purdy 的回答 +1,我可以很容易地理解它 这是我将在普通代码中停止的地方。
嗯,数据类型不是函数。只要您的函数不解包任何数据值(即它只是在 functions/constructors 之间洗牌),您就可以自由地编写它,但是根本没有用于自由点匹配的语法。但是,对于每种数据类型,您只需要一个非无点函数:折叠。在 Haskell 中,数据类型几乎 定义。以相关数据类型的折叠为原语,可以自由改写任意功能点。请注意,实际上有几种可能的“折叠”。对于 [a]
,递归的(来自 Church/Böhm-Berarducci 编码)是 foldr :: (a -> b -> b) -> b -> [a] -> b
。另一种可能的折叠是“case
-but-it's-a-function”,(a -> [a] -> b) -> b -> [a] -> b
,它来自 Scott 编码(然后可以使用 fix
恢复递归,这是另一种“pointful pointfree primitive”),但是,正如@SilvioMayolo 指出的那样,标准库中没有这样的函数。两者都可以,但我们没有预定义后者,所以我们只使用 foldr
.
\[x] -> x
可以写
fst . foldr (\x f -> (snd f x, \_ -> error "got (_ : _ : _) wanted [x]")) (error "got [] wanted [x]", id)
-- I don't care enough to replicate the exact exceptions.
-- this is "flattened" from
let fold [] = (error "got [] wanted [x]", id)
fold (x : xs) = (snd (fold xs) x, \_ -> error "got (_ : _ : _) wanted [x]")
in fst . fold
fold
return一对,基本上是(what to return if this was the entire list, how to transform the head if it wasn't)
。对于 []
,如果那是整个列表,我们希望 return 一个错误,否则在我们点击 []
之前传递元素。对于 x : xs
,如果它前面有一个元素,我们想忽略它并且 return 一个错误,如果没有,我们想把它传递给 snd (fold xs)
,它会检查if xs = []
否则给出错误。我们已经消除了所有匹配项,因此只需将其推入 pointfree.io 即可将 foldr
参数中的 \x f -> _
输出:
behead = fst . foldr (flip flip (const (error "got (_ : _ : _) wanted [x]")) . ((,) .) . flip snd) (error "got [] wanted [x]", id)
ghci> :t behead
behead :: Foldable t => t c -> c
ghci> behead []
*** Exception: got [] wanted [x]
ghci> behead [1]
1
ghci> behead [1, 2]
*** Exception: got (_ : _ : _) wanted [x]
ghci> behead [1..]
*** Exception: got (_ : _ : _) wanted [x]
可爱。
注意:此答案的先前版本使用了“内联”辅助数据类型,主要是因为它只是在我编写它时“想到”了。但是,它无法正确处理无限列表(behead [1..]
会挂起)。这个版本使用内置的对作为辅助数据类型,它有足够的库支持,我不需要内联它们来使它成为无点。内联 (,)
稍微困难一些,从而消除了 fst
和 snd
实现中的 pointfullness,但它仍然是可能的,使用这个新类型:
newtype Pair a b = Pair { unPair :: forall r. (a -> b -> r) -> r }
或者,稍微欺骗一下类型并使用这个:
-- residual pointfullness can be reduced by pointfree.io
\xs -> foldr (\x r f -> f (r (const id) x) (\_ -> error "got (_ : _ : _) wanted [x]")) (\f -> f (error "got [] wanted [x]") id) xs (\x _ _ -> x) undefined
当然,几乎任何东西 都可以 变成无点。棘手的是您将在结果表达式中允许使用哪些函数。如果我们进行模式匹配,我们通常需要一个折叠函数来代替匹配。因此,例如,如果我们在 Maybe a
上进行模式匹配,我们需要将其替换为 maybe
. Similarly, Either a b
patterns can be written in terms of either
.
注意签名中的模式
data Maybe a = Nothing | Just a
maybe :: b -> (a -> b) -> (Maybe a -> b)
Maybe a
有两个构造函数,一个不带参数,另一个带 a
。所以 maybe
接受两个参数:一个是 0 元函数 (b
),另一个接受 a
(a -> b
),然后是 returns 来自 Maybe a -> b
的函数。 either
中存在相同的模式
data Either a b = Left a | Right b
either :: (a -> c) -> (b -> c) -> (Either a b -> c)
两种情况。第一个接受 a
并生成我们想要的任何 c
。第二个接受 b
并生成我们想要的任何 c
。在每种情况下,我们都希望求和类型中的每个可能项都有一个函数。
为了系统地 pointfree 像 \[x] -> x
这样的函数,我们需要一个类似的折叠。 [a]
本质上被声明为
data [a] = [] | a : [a]
所以我们需要一个具有此签名的函数
list :: b -> (a -> [a] -> b) -> ([a] -> b)
现在,flip foldr
接近
flip foldr :: b -> (a -> b -> b) -> ([a] -> b)
但它是递归的。它在 a : [a]
的 [a]
部分调用其提供的函数。我们想要真正的折叠,Haskell 的基础库没有提供。快速 Hoogle 搜索告诉我们这个函数确实存在于一个名为 extra
的包中。当然,这个小例子我们自己写也很简单
list :: b -> (a -> [a] -> b) -> ([a] -> b)
list f g x = case x of
[] -> f
(y:ys) -> g y ys
现在我们可以轻松地将其应用于您的 \[x] -> x
。首先,让我们写下你的函数真正做了什么,包括所有混乱的 undefined
情况(为简洁起见,我将在这里使用 undefined
而不是长错误消息)
func :: [a] -> a
func x = case x of
[] -> undefined
(y:ys) -> case ys of
[] -> y
(_:_) -> undefined
现在每个 case 语句都与每个构造函数完全匹配一次。这是转变为折叠的时机。
func :: [a] -> a
func x = case x of
[] -> undefined
(y:ys) -> list y undefined ys
现在我们也改造外壳
func :: [a] -> a
func x = list undefined (\y -> list y undefined) x
所以我们有
func :: [a] -> a
func = list undefined (\y -> list y undefined)
或者,如果我们真的想为之疯狂
func :: [a] -> a
func = list undefined (flip list undefined)
但是这个函数不在 base
是的,没错。我们有点通过使用不存在的弃牌来作弊。如果我们想系统地做到这一点,我们需要折叠运算符。但是没有它,我们仍然可以将它与 foldr1
拼凑在一起,这足以满足我们的特定目的。
func' :: [a] -> a
func' = foldr1 (const (const undefined))
因此,为了回答您的问题,我们不能总是系统地用 pointfree 替换示例中的模式匹配,除非我们有一个带有正确签名的折叠函数。幸运的是,对于任何 Haskell 98 数据类型(也可能是 GADT,但我没有深入考虑这种可能性),始终可以编写该函数。但即使没有这种支持,我们仍然可以让它发挥作用。
以无点形式编写此代码的一种简单方法是使用折叠,其中累加器状态为以下之一:
Empty:我们还没有看到一个元素;保留它
Full:我们已经看到一个元素;引发错误
如果最终状态是 Empty,我们也会引发错误。这个累加器可以自然地表示为 Maybe
:
fromSingleton :: (Foldable t) => t a -> a
fromSingleton
= fromMaybe (error "empty list")
. foldr (flip maybe (error "plural list") . Just) Nothing
这是我将在普通代码中停止的地方。但是……
如果不想使用辅助数据类型,可以通过使用 Böhm–Berarducci 编码来表示来摆脱 Maybe
:
type Maybe' r a
= r -- ‘Nothing’ continuation
-> (a -> r) -- ‘Just’ continuation
-> r -- Result
just' :: a -> Maybe' r a
-- just' = \ x _n j -> j x
just'
= const -- Ignore ‘Nothing’ continuation
. flip ($) -- Apply ‘Just’ continuation to value
nothing' :: Maybe' r a
-- nothing' = \ n _j -> n
nothing' = const -- Ignore ‘Just’ continuation
maybe' :: r -> (a -> r) -> Maybe' r a -> r
-- maybe' = \ n j k -> k n j
maybe'
= flip -- Apply to ‘Just’ continuation
. flip ($) -- Apply to ‘Nothing’ continuation
fromMaybe' :: r -> Maybe' r r -> r
-- fromMaybe' = \ n k -> k n id
fromMaybe' = flip maybe' id -- Pass ‘id’ as ‘Just’ continuation
但是,我们不能将 Just
全部替换为 just'
,将 maybe
替换为 maybe'
,等等;这些类型无法解决:
> :t fromMaybe' (error "empty list") . foldr (flip maybe' (error "plural list") . just') nothing'
<interactive>:…:…: error:
• Occurs check: cannot construct the infinite type: c ~ Maybe' c c
Expected type: c -> Maybe' c c -> Maybe' c c
Actual type: c -> Maybe' (Maybe' c c) c -> Maybe' c c
• In the first argument of ‘foldr’, namely
‘(flip maybe' (error "plural list") . just')’
In the second argument of ‘(.)’, namely
‘foldr (flip maybe' (error "plural list") . just') nothing'’
In the expression:
fromMaybe' (error "empty list")
. foldr (flip maybe' (error "plural list") . just') nothing'
问题是我们从 Maybe'
延续返回 Maybe'
,并且编译器试图 统一 两种结果类型。一种解决方案是首先 eta-expand 让类型检查器知道我们要在哪里构造一个不同的函数:
> :t fromMaybe' (error "empty list") . foldr (\ x acc -> \ n j -> maybe' (just' x n j) (error "plural list") acc) nothing'
fromMaybe' (error "empty list") . foldr (\ x acc -> \ n j -> maybe' (just' x n j) (error "plural list") acc) nothing'
:: Foldable t => t c -> c
然后我们可以增量重写为 pointfree 形式:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> \ n j
-> maybe'
(just' x n j)
(error "plural list")
acc)
nothing'
-- Move ‘n’ & ‘j’ past ‘error …’ with ‘flip’:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> \ n j
-> flip maybe'
----
(error "plural list")
(just' x n j)
acc)
nothing'
-- Move ‘n’ & ‘j’ past ‘acc’ with ‘flip’ again:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> \ n j
-> flip (flip maybe' (error "plural list")) acc
----
(just' x n j))
nothing'
-- Eta-reduce ‘j’ with composition:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> \ n
-> flip (flip maybe' (error "plural list")) acc
. just' x n)
--
nothing'
-- Eta-reduce ‘n’ with ‘fmap’ (to map “under” an argument):
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> fmap (flip (flip maybe' (error "plural list")) acc)
----
. just' x)
nothing'
-- Move ‘x’ rightward with ‘flip’ on the outside:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc x
----
-> fmap (flip (flip maybe' (error "plural list")) acc)
. just' x))
nothing'
-- Replace composition with ‘fmap’:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc x
-> fmap (fmap (flip (flip maybe' (error "plural list")) acc))
----
(just' x)))
nothing'
-- Eta-reduce ‘x’ with composition:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc
-> fmap (fmap (flip (flip maybe' (error "plural list")) acc))
. just'))
--
nothing'
-- Replace composition with ‘fmap’:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc
-> fmap (fmap (fmap (flip (flip maybe' (error "plural list")) acc)))
----
just'))
nothing'
-- Move ‘acc’ rightward with ‘flip’:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc
-> flip fmap just'
----
(fmap (fmap (flip (flip maybe' (error "plural list")) acc)))))
nothing'
-- Eta-reduce with composition:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip
(flip fmap just'
. fmap . fmap . flip (flip maybe' (error "plural list"))))
-- - -
nothing'
这也是完全无意义的(远不如我们的原始代码可读,但比 pointfree
生成的更好)。事实上,在 pointfree 代码中使用许多小的辅助定义(如 fromMaybe'
而不是内联所有内容是一种很好的做法,但我们可以继续内联它们的定义。
但是,您 不能 天真地内联它们并获得完全相同的类型 — 如果这样做,您将得到 (Foldable t) => t (a -> b) -> a -> b
。完成需要 eta 扩展和重写以获得预期类型 (Foldable t) => t a -> a
.
的地方可能是一个很好的练习
一个相关的问题是
\[x] -> x
http://pointfree.io/好像不能写成无点式的。这是否意味着它不能那样写?如果是这样,理论上的原因是什么?
我只能观察到上面的函数是 head
(或 last
,fwiw)的“残废”版本,它只能在单例列表上运行。事实上,应用于非单例列表,它在 ghci
:
*** Exception: <interactive>:380:5-13: Non-exhaustive patterns in lambda
也许模式上的“非穷尽性”是某些函数不能用无点样式编写的原因?
根据答案编辑:
我没想到我的问题的答案会这么复杂(我觉得我只是认为简短的答案是不,它不能,实际上),所以我需要找时间仔细阅读它们,稍微试验一下,然后全神贯注,否则我无法决定应该接受哪一个。目前,对 Jon Purdy 的回答 +1,我可以很容易地理解它 这是我将在普通代码中停止的地方。
嗯,数据类型不是函数。只要您的函数不解包任何数据值(即它只是在 functions/constructors 之间洗牌),您就可以自由地编写它,但是根本没有用于自由点匹配的语法。但是,对于每种数据类型,您只需要一个非无点函数:折叠。在 Haskell 中,数据类型几乎 定义。以相关数据类型的折叠为原语,可以自由改写任意功能点。请注意,实际上有几种可能的“折叠”。对于 [a]
,递归的(来自 Church/Böhm-Berarducci 编码)是 foldr :: (a -> b -> b) -> b -> [a] -> b
。另一种可能的折叠是“case
-but-it's-a-function”,(a -> [a] -> b) -> b -> [a] -> b
,它来自 Scott 编码(然后可以使用 fix
恢复递归,这是另一种“pointful pointfree primitive”),但是,正如@SilvioMayolo 指出的那样,标准库中没有这样的函数。两者都可以,但我们没有预定义后者,所以我们只使用 foldr
.
\[x] -> x
可以写
fst . foldr (\x f -> (snd f x, \_ -> error "got (_ : _ : _) wanted [x]")) (error "got [] wanted [x]", id)
-- I don't care enough to replicate the exact exceptions.
-- this is "flattened" from
let fold [] = (error "got [] wanted [x]", id)
fold (x : xs) = (snd (fold xs) x, \_ -> error "got (_ : _ : _) wanted [x]")
in fst . fold
fold
return一对,基本上是(what to return if this was the entire list, how to transform the head if it wasn't)
。对于 []
,如果那是整个列表,我们希望 return 一个错误,否则在我们点击 []
之前传递元素。对于 x : xs
,如果它前面有一个元素,我们想忽略它并且 return 一个错误,如果没有,我们想把它传递给 snd (fold xs)
,它会检查if xs = []
否则给出错误。我们已经消除了所有匹配项,因此只需将其推入 pointfree.io 即可将 foldr
参数中的 \x f -> _
输出:
behead = fst . foldr (flip flip (const (error "got (_ : _ : _) wanted [x]")) . ((,) .) . flip snd) (error "got [] wanted [x]", id)
ghci> :t behead
behead :: Foldable t => t c -> c
ghci> behead []
*** Exception: got [] wanted [x]
ghci> behead [1]
1
ghci> behead [1, 2]
*** Exception: got (_ : _ : _) wanted [x]
ghci> behead [1..]
*** Exception: got (_ : _ : _) wanted [x]
可爱。
注意:此答案的先前版本使用了“内联”辅助数据类型,主要是因为它只是在我编写它时“想到”了。但是,它无法正确处理无限列表(behead [1..]
会挂起)。这个版本使用内置的对作为辅助数据类型,它有足够的库支持,我不需要内联它们来使它成为无点。内联 (,)
稍微困难一些,从而消除了 fst
和 snd
实现中的 pointfullness,但它仍然是可能的,使用这个新类型:
newtype Pair a b = Pair { unPair :: forall r. (a -> b -> r) -> r }
或者,稍微欺骗一下类型并使用这个:
-- residual pointfullness can be reduced by pointfree.io
\xs -> foldr (\x r f -> f (r (const id) x) (\_ -> error "got (_ : _ : _) wanted [x]")) (\f -> f (error "got [] wanted [x]") id) xs (\x _ _ -> x) undefined
当然,几乎任何东西 都可以 变成无点。棘手的是您将在结果表达式中允许使用哪些函数。如果我们进行模式匹配,我们通常需要一个折叠函数来代替匹配。因此,例如,如果我们在 Maybe a
上进行模式匹配,我们需要将其替换为 maybe
. Similarly, Either a b
patterns can be written in terms of either
.
注意签名中的模式
data Maybe a = Nothing | Just a
maybe :: b -> (a -> b) -> (Maybe a -> b)
Maybe a
有两个构造函数,一个不带参数,另一个带 a
。所以 maybe
接受两个参数:一个是 0 元函数 (b
),另一个接受 a
(a -> b
),然后是 returns 来自 Maybe a -> b
的函数。 either
data Either a b = Left a | Right b
either :: (a -> c) -> (b -> c) -> (Either a b -> c)
两种情况。第一个接受 a
并生成我们想要的任何 c
。第二个接受 b
并生成我们想要的任何 c
。在每种情况下,我们都希望求和类型中的每个可能项都有一个函数。
为了系统地 pointfree 像 \[x] -> x
这样的函数,我们需要一个类似的折叠。 [a]
本质上被声明为
data [a] = [] | a : [a]
所以我们需要一个具有此签名的函数
list :: b -> (a -> [a] -> b) -> ([a] -> b)
现在,flip foldr
接近
flip foldr :: b -> (a -> b -> b) -> ([a] -> b)
但它是递归的。它在 a : [a]
的 [a]
部分调用其提供的函数。我们想要真正的折叠,Haskell 的基础库没有提供。快速 Hoogle 搜索告诉我们这个函数确实存在于一个名为 extra
的包中。当然,这个小例子我们自己写也很简单
list :: b -> (a -> [a] -> b) -> ([a] -> b)
list f g x = case x of
[] -> f
(y:ys) -> g y ys
现在我们可以轻松地将其应用于您的 \[x] -> x
。首先,让我们写下你的函数真正做了什么,包括所有混乱的 undefined
情况(为简洁起见,我将在这里使用 undefined
而不是长错误消息)
func :: [a] -> a
func x = case x of
[] -> undefined
(y:ys) -> case ys of
[] -> y
(_:_) -> undefined
现在每个 case 语句都与每个构造函数完全匹配一次。这是转变为折叠的时机。
func :: [a] -> a
func x = case x of
[] -> undefined
(y:ys) -> list y undefined ys
现在我们也改造外壳
func :: [a] -> a
func x = list undefined (\y -> list y undefined) x
所以我们有
func :: [a] -> a
func = list undefined (\y -> list y undefined)
或者,如果我们真的想为之疯狂
func :: [a] -> a
func = list undefined (flip list undefined)
但是这个函数不在 base
是的,没错。我们有点通过使用不存在的弃牌来作弊。如果我们想系统地做到这一点,我们需要折叠运算符。但是没有它,我们仍然可以将它与 foldr1
拼凑在一起,这足以满足我们的特定目的。
func' :: [a] -> a
func' = foldr1 (const (const undefined))
因此,为了回答您的问题,我们不能总是系统地用 pointfree 替换示例中的模式匹配,除非我们有一个带有正确签名的折叠函数。幸运的是,对于任何 Haskell 98 数据类型(也可能是 GADT,但我没有深入考虑这种可能性),始终可以编写该函数。但即使没有这种支持,我们仍然可以让它发挥作用。
以无点形式编写此代码的一种简单方法是使用折叠,其中累加器状态为以下之一:
Empty:我们还没有看到一个元素;保留它
Full:我们已经看到一个元素;引发错误
如果最终状态是 Empty,我们也会引发错误。这个累加器可以自然地表示为 Maybe
:
fromSingleton :: (Foldable t) => t a -> a
fromSingleton
= fromMaybe (error "empty list")
. foldr (flip maybe (error "plural list") . Just) Nothing
这是我将在普通代码中停止的地方。但是……
如果不想使用辅助数据类型,可以通过使用 Böhm–Berarducci 编码来表示来摆脱 Maybe
:
type Maybe' r a
= r -- ‘Nothing’ continuation
-> (a -> r) -- ‘Just’ continuation
-> r -- Result
just' :: a -> Maybe' r a
-- just' = \ x _n j -> j x
just'
= const -- Ignore ‘Nothing’ continuation
. flip ($) -- Apply ‘Just’ continuation to value
nothing' :: Maybe' r a
-- nothing' = \ n _j -> n
nothing' = const -- Ignore ‘Just’ continuation
maybe' :: r -> (a -> r) -> Maybe' r a -> r
-- maybe' = \ n j k -> k n j
maybe'
= flip -- Apply to ‘Just’ continuation
. flip ($) -- Apply to ‘Nothing’ continuation
fromMaybe' :: r -> Maybe' r r -> r
-- fromMaybe' = \ n k -> k n id
fromMaybe' = flip maybe' id -- Pass ‘id’ as ‘Just’ continuation
但是,我们不能将 Just
全部替换为 just'
,将 maybe
替换为 maybe'
,等等;这些类型无法解决:
> :t fromMaybe' (error "empty list") . foldr (flip maybe' (error "plural list") . just') nothing'
<interactive>:…:…: error:
• Occurs check: cannot construct the infinite type: c ~ Maybe' c c
Expected type: c -> Maybe' c c -> Maybe' c c
Actual type: c -> Maybe' (Maybe' c c) c -> Maybe' c c
• In the first argument of ‘foldr’, namely
‘(flip maybe' (error "plural list") . just')’
In the second argument of ‘(.)’, namely
‘foldr (flip maybe' (error "plural list") . just') nothing'’
In the expression:
fromMaybe' (error "empty list")
. foldr (flip maybe' (error "plural list") . just') nothing'
问题是我们从 Maybe'
延续返回 Maybe'
,并且编译器试图 统一 两种结果类型。一种解决方案是首先 eta-expand 让类型检查器知道我们要在哪里构造一个不同的函数:
> :t fromMaybe' (error "empty list") . foldr (\ x acc -> \ n j -> maybe' (just' x n j) (error "plural list") acc) nothing'
fromMaybe' (error "empty list") . foldr (\ x acc -> \ n j -> maybe' (just' x n j) (error "plural list") acc) nothing'
:: Foldable t => t c -> c
然后我们可以增量重写为 pointfree 形式:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> \ n j
-> maybe'
(just' x n j)
(error "plural list")
acc)
nothing'
-- Move ‘n’ & ‘j’ past ‘error …’ with ‘flip’:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> \ n j
-> flip maybe'
----
(error "plural list")
(just' x n j)
acc)
nothing'
-- Move ‘n’ & ‘j’ past ‘acc’ with ‘flip’ again:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> \ n j
-> flip (flip maybe' (error "plural list")) acc
----
(just' x n j))
nothing'
-- Eta-reduce ‘j’ with composition:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> \ n
-> flip (flip maybe' (error "plural list")) acc
. just' x n)
--
nothing'
-- Eta-reduce ‘n’ with ‘fmap’ (to map “under” an argument):
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(\ x acc
-> fmap (flip (flip maybe' (error "plural list")) acc)
----
. just' x)
nothing'
-- Move ‘x’ rightward with ‘flip’ on the outside:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc x
----
-> fmap (flip (flip maybe' (error "plural list")) acc)
. just' x))
nothing'
-- Replace composition with ‘fmap’:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc x
-> fmap (fmap (flip (flip maybe' (error "plural list")) acc))
----
(just' x)))
nothing'
-- Eta-reduce ‘x’ with composition:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc
-> fmap (fmap (flip (flip maybe' (error "plural list")) acc))
. just'))
--
nothing'
-- Replace composition with ‘fmap’:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc
-> fmap (fmap (fmap (flip (flip maybe' (error "plural list")) acc)))
----
just'))
nothing'
-- Move ‘acc’ rightward with ‘flip’:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip (\ acc
-> flip fmap just'
----
(fmap (fmap (flip (flip maybe' (error "plural list")) acc)))))
nothing'
-- Eta-reduce with composition:
fromSingleton
= fromMaybe' (error "empty list")
. foldr
(flip
(flip fmap just'
. fmap . fmap . flip (flip maybe' (error "plural list"))))
-- - -
nothing'
这也是完全无意义的(远不如我们的原始代码可读,但比 pointfree
生成的更好)。事实上,在 pointfree 代码中使用许多小的辅助定义(如 fromMaybe'
而不是内联所有内容是一种很好的做法,但我们可以继续内联它们的定义。
但是,您 不能 天真地内联它们并获得完全相同的类型 — 如果这样做,您将得到 (Foldable t) => t (a -> b) -> a -> b
。完成需要 eta 扩展和重写以获得预期类型 (Foldable t) => t a -> a
.