[英]How to write this polyvariadic composition function in Haskell?
注意:這是作者刪除的另一個問題的重新發布。 這是原始問題:
我在Javascript中有這個polyvariadic comp
函數,並且想知道Haskell中的類似實現是否可行。 我最感興趣的是comp
的類型:
const comp = f => Object.assign(
g => comp([g].concat(f)),
{run: x => f.reduce((acc, h) => h(acc), x)}
);
const inc = n => n + 1;
const sqr = n => n * n;
const repeatStr = s => n => Array(n + 1).join(s);
comp(repeatStr("*")) (inc) (sqr).run(2); // "*****"
comp(repeatStr("*"))
(inc)
(inc)
(inc)
(inc)
(inc).run(0); // "*****"
comp
構建一個異構數組,通常在Haskell中沒有類型。 我想這樣的可變函數在其返回類型中必須是多態的。 但是,到目前為止,這項任務超出了我的Haskell知識。 任何線索都會有所幫助。
上下文
我使用Javascript運行時類型檢查器,以便我可以以類型安全的方式在comp
中構建數組。 它需要顯式類型注釋,並且僅支持參數和秩-2多態。
你是對的。 您無法在Haskell (1)中構建可組合函數的異構列表。 但是,您可以為可組合函數創建自己的列表數據類型,如下所示:
{-# LANGUAGE GADTs #-}
data Comp a b where
Id :: Comp a a
Comp :: Comp b c -> (a -> b) -> Comp a c
run :: Comp a b -> a -> b
run Id = id
run (Comp g f) = run g . f
Id
構造函數類似於[]
, Comp
構造函數類似於:
但是參數被翻轉。
接下來,我們使用varargs模式創建一個多變量函數。 為此,我們定義了一個類型類:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
class Chain a b c | c -> a where
chain :: Comp a b -> c
請注意,我們的狀態是Comp bc
,我們的結果是Comp bc
或帶有另一個函數(a -> b)
作為輸入的函數,並將其與我們的狀態組合以生成一個名為r
的新Chain
,其狀態為Comp ac
。 我們現在為這些定義實例:
{-# LANGUAGE FlexibleInstances #-}
instance c ~ c' => Chain b c (Comp b c') where
chain = id
instance Chain a c r => Chain b c ((a -> b) -> r) where
chain g f = chain (Comp g f)
comp :: Chain b b c => c
comp = chain Id
現在可以將comp
函數定義為chain Id
(即具有空列表Id
作為其狀態的鏈)。 我們最終可以像在JavaScript中一樣使用這個comp
函數:
inc :: Int -> Int
inc = (+1)
sqr :: Int -> Int
sqr x = x * x
repeatStr :: String -> Int -> String
repeatStr s x = concat (replicate x s)
example1 :: String
example1 = comp (repeatStr "*") inc sqr `run` 2
example2 :: String
example2 = comp (repeatStr "*") inc inc inc inc inc `run` 0
把它們放在一起:
{-# LANGUAGE GADTs, MultiParamTypeClasses, FunctionalDependencies,
FlexibleInstances #-}
data Comp a b where
Id :: Comp a a
Comp :: Comp b c -> (a -> b) -> Comp a c
run :: Comp a b -> a -> b
run Id = id
run (Comp g f) = run g . f
class Chain a b c | c -> a where
chain :: Comp a b -> c
instance c ~ c' => Chain b c (Comp b c') where
chain = id
instance Chain a c r => Chain b c ((a -> b) -> r) where
chain g f = chain (Comp g f)
comp :: Chain b b c => c
comp = chain Id
inc :: Int -> Int
inc = (+1)
sqr :: Int -> Int
sqr x = x * x
repeatStr :: String -> Int -> String
repeatStr s x = concat (replicate x s)
example1 :: String
example1 = comp (repeatStr "*") inc sqr `run` 2
example2 :: String
example2 = comp (repeatStr "*") inc inc inc inc inc `run` 0
如您所見, comp
的類型是Chain bbc => c
。 要定義Chain
類型,我們需要MultiParamTypeClasses
和FunctionalDependencies
。 要使用它,我們需要FlexibleInstances
。 因此,您需要一個復雜的JavaScript運行時類型檢查器才能正確鍵入check comp
。
編輯:正如naomik和Daniel Wagner在評論中指出的那樣,您可以使用實際函數而不是可組合函數列表作為comp
狀態的內部表示。 例如,在JavaScript中:
const comp = run => Object.assign(g => comp(x => g(run(x))), {run});
同樣,在Haskell中:
{-# LANGUAGE GADTs, MultiParamTypeClasses, FunctionalDependencies,
FlexibleInstances #-}
newtype Comp a b = Comp { run :: a -> b }
class Chain a b c | c -> a where
chain :: Comp a b -> c
instance c ~ c' => Chain b c (Comp b c') where
chain = id
instance Chain a c r => Chain b c ((a -> b) -> r) where
chain g f = chain (Comp (run g . f))
comp :: Chain b b c => c
comp = chain (Comp id)
請注意,即使我們不再使用GADT,我們仍然需要GADTs
語言擴展,以便在Chain
的第一個實例中使用等式約束c ~ c'
。 另外,你可以看到run g . f
run g . f
已從run
的定義轉移到Chain
的第二個實例。 類似地, id
已從run
的定義移到comp
的定義中。
(1)您可以使用存在類型在Haskell中創建異構函數列表,但它們不具有可組合的附加約束。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.