得意分野 - haskell 関数 型




どのように多変数のhaskell関数を作成するには? (4)

KennyTMの答えは素晴らしいです。 以下は、より良い説明を与えるためにsumOf 1 4 7 10 :: Integerのexecプロセスの例です。

sumOf 1 4 7 10
(( \ x -> ( sumOf . (x +) . toInteger ) 1 ) 4 7 10
((sumOf . (1 + ) . toInteger) 4 ) 7 10
( sumOf 5 ) 7 10
( sumOf . (5 + ) . toInteger ) 7 10
sumOf 12 10
sumOf . (12 + ) . toInteger 10
sumof 22
id 22
22

私は任意の数の引数(同じ型のすべて)を取る関数が必要です、それらと何かをし、後で結果を返します。 私の特定のケースでは、議論のリストは実行不可能です。

私がhaskellライブラリを見ているうちに、関数printf (モジュールText.Printfから)が同様のトリックを使用していることがText.Printf 。 残念ながら、私はソースを見てその魔法を理解できませんでした。

誰かがこれを達成する方法、または少なくともいくつかのウェブページ/論文/これの良い記述がどこにあるのかを説明することはできますか?

動機:

私がこれを必要とする理由は、とても単純です。 学校(コンピューターサイエンスクラス)では、数学的表現を「記録」するか、それを文字列として表現する(独自のデータ型に対してNum / Real / etcのインスタンスを書くことによって)ことができるモジュールを書く必要があります。それに関する様々な操作。

このデータ型には、変数の特別なコンストラクタが含まれています。このコンストラクタは、値などで置き換えることができます。 その目的の1つは、いくつかの変数(型(Char,Rational)ペア)でそのような式を取り、その式の結果を計算する関数を書くことです。 私たちは機能の目標をどのように表現するかを見なければなりません。 (私の考え:この関数は、関数で定義された変数と同じくらい多くの引数を取る別の関数を返します - 不可能と思われます)。


delnanが参照している記事からリンクされている例を見てみまし 。 少しそれを見て、私は最後に何が起こっているかを理解すると思います:

このタイプのクラスから始まります:

class BuildList a r  | r-> a where
    build' :: [a] -> a -> r

パイプ(|)の後のビットは関数の依存関係です。 それは、 aによって表される型は、 rによって表される型によって決定され得ることを示している。 つまり、同じr (戻り値の型)を持つBuildList型の2つのインスタンスを定義することはできませんが、異なるものがaます。

build'機能が実際に使用されている場所へ少し前にジャンプ:

> build True :: [Bool]

buildは空のリストを最初のパラメータとしてbuild'を呼び出すだけなので、これは次のようになります:

> build' [] True :: [Bool]

この例では、 build'は明らかにリストを返しています。 関数の依存関係のため、このBuildList型クラスのインスタンスにのみバインドできます。

instance BuildList a [a] where
    build' l x = reverse$ x:l

かなり簡単。 2番目の例はもっと面白いです。 buildの定義を拡張build 、次のようになります。

> build' [] True False :: [Bool]

この場合、 build'タイプは何ですか? さて、Haskellの優先ルールは、上記のように書くこともできます:

> (build' [] True) False :: [Bool]

今度は、 build' 2つのパラメータを渡し、その式の結果を値 'False'のパラメータに適用することが明らかになりました。 言い換えれば、式(build' [] True)Bool -> [Bool]型の関数を返すことが期待されます。 BuildListリストのBuildListクラスの2番目のインスタンスにBuildListます。

instance BuildList a r => BuildList a (a->r) where
    build' l x y = build'(x:l) y

この呼び出しでは、 l = []x = Truey = Falseなので、定義はbuild' [True] False :: [Bool]ます。 そのシグネチャはbuild'最初のインスタンスにバインドされており、そこからどこに行くのかはかなり明白です。


printfの重要なポイントは、文字列または関数を返すことです。 http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.htmlからコピーされ、

printf :: (PrintfType r) => String -> r
printf fmts = spr fmts []

class PrintfType t where
    spr :: String -> [UPrintf] -> t

instance (IsChar c) => PrintfType [c] where
    spr fmts args = map fromChar (uprintf fmts (reverse args))

instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where
    spr fmts args = \a -> spr fmts (toUPrintf a : args)

私たちが取り出すことができる基本的な構造は

variadicFunction :: VariadicReturnClass r => RequiredArgs -> r
variadicFunction reqArgs = variadicImpl reqArgs mempty

class VariadicReturnClass r where
   variadicImpl :: RequiredArgs -> AccumulatingType -> r

instance VariadicReturnClass ActualReturnType where
   variadicImpl reqArgs acc = constructActualResult reqArgs acc

instance (ArgClass a, VariadicReturnClass r) => VariadicReturnClass (a -> r) where
   variadicImpl reqArgs acc = \a -> variadicImpl reqArgs (specialize a `mappend` acc)

例えば:

class SumRes r where 
    sumOf :: Integer -> r

instance SumRes Integer where
    sumOf = id

instance (Integral a, SumRes r) => SumRes (a -> r) where
    sumOf x = sumOf . (x +) . toInteger

それから、

*Main> sumOf 1 :: Integer
1
*Main> sumOf 1 4 7 10 :: Integer
22
*Main> sumOf 1 4 7 10 0 0  :: Integer
22
*Main> sumOf 1 4 7 10 2 5 8 22 :: Integer
59

多くの人がvariadic関数を作成する方法を教えていますが、この場合、実際には[(Char、Rational)]という型のリストを使う方が良いでしょう。





polyvariadic