与往常一样,让我们将类型注释放在各处。
-- foldr ($) "hello" [(+) 1, length]
($) :: (a -> b) -> a -> b
"hello" :: [Char]
(+) 1 :: Num a => a -> a
length :: [a] -> Int
[(+) 1, length] :: ?!
本机 Haskell 列表不能包含不同类型的项目。
那么让我们退后一步。我会用<
and >
表示“我们想要的列表式的东西”。我们不想要随机类型的集合。< (+) 1, length >
没关系,但是< length, (+) 1 >
不是,或者更确切地说我们需要一个实例Num [a]
。类似地,如果我们有两个以上的项目:每个项目的类型必然与其邻居相关。此外,整个列表的类型仅与第一个和最后一个成员的类型相关:起始和结束类型是什么?
我们可以使用 GADT 来做到这一点:
{-# LANGUAGE GADTs #-}
module SO26565306 where
data F a b where
FNil :: F a a
(:&) :: (b -> c) -> F a b -> F a c
infixr 5 :&
runF :: F a b -> a -> b
runF FNil = id
runF (f :& fs) = f . runF fs
f :: F [a] Int
f = (+) 1 :& length :& FNil
ghci> runF f "hello"
6
价值f
是你想要的实现< (+) 1, length >
"list".
还有一些进一步的阐述F
可能的 -Functor
and Category
例如,但我真的不认为它有多大用处。我们所做的只是在普通函数组合上人为地强加了一种数据结构。我们甚至不能使用 GHC重载列表表示法 https://ghc.haskell.org/trac/ghc/wiki/OverloadedLists,这(还?)不够灵活。此外,插入所有 GADT 构造函数将阻止优化,几乎肯定包括列表融合。 (我没有仔细实验或思考过。)
回答你的问题
是的,可以定义一个 Haskell 函数,它接受不同但可组合类型的函数集合,并生成它们的组合。我演示的集合类型需要GADTs
扩展名,即不适用于拥抱 https://stackoverflow.com/questions/11158023/how-to-use-gadts-in-hugs,您似乎正在使用的编译器。此外,您实际上无法对集合做太多事情。我还没有证明这一点,但我会断言,你无法对 type 的值做任何事情F a b
你不能用 type 的值来做a -> b
,而不是通过模式匹配将其分解为其组件函数。
换句话说,你所问的问题确实可以在 Haskell 中表达,只是不清楚以这种方式这样做有什么好处。
其他问题
正如我们在评论中讨论的那样,您似乎正在寻找 Clojure 传感器的 Haskell 类比。您想就该主题提出一个新问题吗?它比这个更精确,重点也不同。
底线
为什么不直接使用((+) 1) . length
?