J languageで算術演算がどのように動作するかを、リストにバイナリ演算を自動的に分配するHaskell関数を記述しようとしています。 これは、深さのネストされたリストで動作する「深いzipWith
」と考えることができます(非リストや異なる深度のリストを含む)。たとえば :異なる長さの入れ子リストをサポートする "zipWith"がオーバーロードされました
distr (+) 1 10 === 11 -- Non-list values are added together
distr (+) [1,2] 10 === [11,12] -- Non-list values distribute over lists
distr (+) [1,2] [10,20] === [11,22] -- Two lists get zipped
distr (+) [[1,2],[3,4]] [[10,20],[30,40]] === [[11,22],[33,44]] -- Nested lists get zipped
リストはzipWith
と同じように、切り捨てられ得るが、これは重要ではありません。
さて、私はすでにこれを書いた:
{-# LANGUAGE
MultiParamTypeClasses,
FunctionalDependencies,
UndecidableInstances,
FlexibleInstances
#-}
class Distr a b c x y z | a b c x y -> z
where distr :: (a -> b -> c) -> (x -> y -> z)
instance Distr a b c a b c where distr = id
instance {-# OVERLAPPING #-}
(Distr a b c x y z) => Distr a b c [x] [y] [z]
where distr = zipWith . distr
instance (Distr a b c x y z) => Distr a b c [x] y [z]
where distr f xs y = map (\x -> distr f x y) xs
instance (Distr a b c x y z) => Distr a b c x [y] [z]
where distr f x ys = map (\y -> distr f x y) ys
これは、関数distr :: (Distr a b c x y z) => (a -> b -> c) -> (x -> y -> z)
、およびネストされたリスト上のDistr
のいくつかの事例で6パラメータの型クラスDistr
を定義します。 これは上の例でうまくいきますが、不等ネスト深度のリスト上での振る舞いは、正確には私が望むものではありません。 それは、この(あなたが(+)
に型注釈を追加し、両方のリストならば働く)を行います。私が欲しいもの
distr (+) [[1,2],[3,4]] [10,20] === [[11,12],[23,24]] -- Zip and distribute
Try it here. はこれです:
distr (+) [[1,2],[3,4]] [10,20] === [[11,22],[13,24]] -- Distribute and zip
現在の実装では、そのの1までzipWith
を適用します引き数はリスト以外の値で、それが他のリストに分散されます。 等しい入れ子の深さに達するまで、1つの引数(より少ないリストレイヤーを持つもの)を他のものよりも優先して配布し、zipWith
を使用してリスト以外の値に減らすことをお勧めします。
私の質問は次のとおりです。第2の種類の動作を達成できますか? 私は、現在のソリューションと同様に、Haskellに演算子のタイプと各引数を明示的に伝える必要があるソリューションに満足しています。 リストを入力として扱うオペレータにはdistr
を呼び出さないので、そのケースを処理する必要はありません。 しかし、タイプヒントとなるdistr
に余分な引数を与えたくない場合や、さまざまなユースケースに応じて異なるバージョンのdistr
をいくつか追加したいとします。 私の問題はこの方法で解決できると知っていますが、私はそれが必要でない解決策を好むでしょう。
これは、私はHaskellのにコンパイル難解な関数型プログラミング言語のためにそれを使用してい –
@BenjaminHodgsonを行うには奇妙なことのように思えます。コンパイラはこの関数を使用するコードを生成します。私がここで説明するように実装することができれば、私たちの型推論の部分的な書き直しを省くことができます。 :P – Zgarb
代わりに、あなたのコンパイラのcodegenと型チェックフェーズを分けることをお勧めします。 –