簡體   English   中英

如何在Haskell中編寫這個polyvariadic組合函數?

[英]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類型,我們需要MultiParamTypeClassesFunctionalDependencies 要使用它,我們需要FlexibleInstances 因此,您需要一個復雜的JavaScript運行時類型檢查器才能正確鍵入check comp


編輯:正如naomikDaniel 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM