15

Haskellで多変量関数を書くことについてのこの記事を読んだ後、私は自分自身のいくつかを書こうとしました。

最初はそれを一般化しようと思ったので、与えられた引数を折りたたむことで可変個引数関数を返す関数を作成できました。

{-# OPTIONS -fglasgow-exts #-}
module Collapse where
class Collapse a r | r -> a where
  collapse :: (a -> a -> a) -> a -> r
instance Collapse a a where
  collapse _ = id
instance (Collapse a r) => Collapse a (a -> r) where
  collapse f a a' = collapse f (f a a')

しかし、コンパイラはそれを気に入らなかった:

Collapse.hs:5:9:
    Functional dependencies conflict between instance declarations:
      instance Collapse a a -- Defined at Collapse.hs:5:9-20
      instance (Collapse a r) => Collapse a (a -> r)
        -- Defined at Collapse.hs:7:9-43

ただし、戻って最終結果のラッパータイプを追加すると、次のように機能しました。

module Collapse where
class Collapse a r | r -> a where
  collapse :: (a -> a -> a) -> a -> r
data C a = C a
instance Collapse a (C a) where
  collapse _ = C . id
instance (Collapse a r) => Collapse a (a -> r) where
  collapse f a a' = collapse f (f a a')
sum :: (Num a, Collapse a r) => a -> r
sum = collapse (+)

この変更を行うと、正常にコンパイルされ、でcollapse関数を使用できるようになりましたghci

ghci> let C s = Collapse.sum 1 2 3 in s
6

最終結果にラッパータイプが必要な理由がわかりません。誰かがそれを説明できれば、私はそれを高く評価します。コンパイラーが機能依存性に問題があると言っているのはわかりますが、fundepsの適切な使用法についてはまだ理解していません。

後で、私は別の方法を取り、リストを取得して値を返す関数の可変個引数関数発生器を定義しようとしました。私は同じコンテナトリックをしなければならず、また許可しなければなりませんでしたUndecidableInstances

{-# OPTIONS -fglasgow-exts #-}
{-# LANGUAGE UndecidableInstances #-}
module Variadic where
class Variadic a b r | r -> a, r -> b where
  variadic :: ([a] -> b) -> r
data V a = V a
instance Variadic a b (V b) where
  variadic f = V $ f []
instance (Variadic a b r) => Variadic a b (a -> r) where
  variadic f a = variadic (f . (a:))
list :: Variadic a [a] r => r
list = variadic . id
foldl :: (Variadic b a r) => (a -> b -> a) -> a -> r
foldl f a = variadic (Prelude.foldl f a)

UndecidableInstancesコンパイラが私のインスタンス宣言が違法であると文句を言うことを許可せずに:

Variadic.hs:7:0:
    Illegal instance declaration for `Variadic a b (V b)'
        (the Coverage Condition fails for one of the functional dependencies;
         Use -XUndecidableInstances to permit this)
    In the instance declaration for `Variadic a b (V b)'

Variadic.hs:9:0:
    Illegal instance declaration for `Variadic a b (a -> r)'
        (the Coverage Condition fails for one of the functional dependencies;
         Use -XUndecidableInstances to permit this)
    In the instance declaration for `Variadic a b (a -> r)'

ただし、コンパイルすると、ghciで正常に使用できます。

ghci> let V l = Variadic.list 1 2 3 in l
[1,2,3]
ghci> let vall p = Variadic.foldl (\b a -> b && (p a)) True
ghci> :t vall
vall :: (Variadic b Bool r) => (b -> Bool) -> r
ghci> let V b = vall (>0) 1 2 3 in b
True

が探しているのは、最終値のコンテナータイプが必要な理由と、さまざまな機能依存性がすべて必要な理由の説明だと思います。

また、これは奇妙に思えました:

ghci> let vsum = Variadic.foldl (+) 0

<interactive>:1:10:
    Ambiguous type variables `a', `r' in the constraint:
      `Variadic a a r'
        arising from a use of `Variadic.foldl' at <interactive>:1:10-29
    Probable fix: add a type signature that fixes these type variable(s)

<interactive>:1:10:
    Ambiguous type variable `a'in the constraint:
      `Num a' arising from the literal `0' at <interactive>:1:29
    Probable fix: add a type signature that fixes these type variable(s)
ghci> let vsum' = Variadic.foldl (+) 
ghci> :t vsum'
(Num a, Variadic a a r) => t -> a -> r
ghci> :t vsum' 0
(Num a, Variadic a a r) => a -> r
ghci> let V s = vsum' 0 1 2 3 in s
6

それは許可の結果UndecidableInstancesだと思いますが、わかりません。何が起こっているのかをもっとよく理解したいと思います。

4

3 に答える 3

8

関数の依存関係の背後にある考え方は、次のような宣言で

class Collapse a r | r -> a where
  ...

r -> aビットは、によってa一意に決定されることを示していrます。instance Collapse (a -> r) (a -> r)したがって、 and を持つことはできませんinstance Collapse a (a -> r)。全体像については、instance Collapse (a -> r) (a -> r)次のことに注意してください。instance Collapse a a

言い換えれば、コードはinstance Collapse t t(型変数の名前はもちろん重要ではありません) とinstance Collapse a (a -> r). 最初のインスタンス宣言で(a -> r)forを代入すると、 が得られます。クラス宣言では、最初の型パラメーターが 2 番目の型パラメーターから推定可能であることが示されているため、これはと等しい 2 番目の型パラメーターを持つの唯一のインスタンスです。次に、 を確立しようとします。これにより、2 番目の型パラメーターが である の別のインスタンスが追加されます。したがって、GHCは文句を言います。tinstance Collapse (a -> r) (a -> r)Collapse(a -> r)instance a (a -> r)Collapse(a -> r)

于 2010-01-28T17:49:48.053 に答える
5

これをまだ試している場合は、ラッパー型や決定不能なインスタンスを必要とせずに、リストを取る関数から多変量関数を構築する例を次に示します。

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}

class Variadic a b r | r -> a where
    variadic :: ([a] -> b) -> r

instance Variadic a b (a -> b) where
    variadic f x = f [x]

instance (Variadic a b (a -> r)) => Variadic a b (a -> a -> r) where
    variadic f x y = variadic (f . (x:)) y

vList :: (Variadic a [a] r) => r
vList = variadic id

vFoldl :: (Variadic b a r) => (a -> b -> a) -> a -> r
vFoldl f z = variadic (foldl f z)

vConcat :: (Variadic [a] [a] r) => r
vConcat = vFoldl (++) []

main = do
    putStrLn $ vConcat "abc" "def" "ghi" "jkl"
    putStrLn $ vList 'x' 'y' 'z'
    if vFoldl (&&) True True True True then putStrLn "Yes" else putStrLn "No"
    if vFoldl (&&) True True False True then putStrLn "Yes" else putStrLn "No"

このアプローチの欠点は、型チェッカーが結果の型を推測できなければならない (または注釈を付ける必要がある) ことと、多相的な数値定数ではうまく機能しないことです。両方の問題の理由は、あなたが言及した記事で説明されています。それが役立つかどうかはわかりませんが、以前に多変量関数をいじっていて、この質問を思い出しました。

于 2010-01-31T07:32:37.233 に答える
4

MichałMarczykは、fundepsとインスタンスのマッチングの問題について完全に正しいので、ラッパータイプは簡単に修正できるようです。一方、すでにOlegのサイトを読んでいる場合は、うさぎの穴を深く掘り下げて、 「関数ではない任意のタイプ」のインスタンスを作成してみてください。

UndecidableInstancesに関する限り、カバレッジ条件はここで説明されています; インスタンスが失敗する理由は明らかです。ここでの「決定不能」という言葉は、「停止問題は決定不能」とほぼ同じ意味で決定不能を意味することに注意してください。つまり、無限ループに送る可能性のあるコードを無謀に解決しようとするようにGHCに指示していることに注意してください。それは大丈夫だというあなたの主張だけに基づいています。きちんとしたアイデアをハッキングするのは楽しいですが、GHCの人間の初回通過タイプチェッカーになることに同意することは、私が個人的に面倒だと感じる負担です。

于 2010-01-28T18:23:42.903 に答える