私が書いているライブラリでは、次のような (ただし、より一般的な) クラスを書くのが一見エレガントであることがuncurry
わかりました。必要に応じて):fanin
{-# LANGUAGE TypeOperators, TypeFamilies,MultiParamTypeClasses, FlexibleInstances #-}
import Prelude hiding(uncurry)
import qualified Prelude
class Uncurry t r where
type t :->-> r
uncurry :: (t :->-> r) -> t -> r
instance Uncurry () r where
type () :->-> r = r
uncurry = const
instance Uncurry (a,b) r where
type (a,b) :->-> r = a -> b -> r
uncurry = Prelude.uncurry
instance (Uncurry b c, Uncurry a c)=> Uncurry (Either a b) c where
type Either a b :->-> c = (a :->-> c, b :->-> c)
uncurry (f,g) = either (uncurry f) (uncurry g)
私は通常、Edward Kmett のcategories
パッケージ (上記のリンク) を参照して、この種のことを理解していますが、そのパッケージには、fanin と uncurry がそれぞれ CoCartesian クラスと CCC クラスに分けられています。
BiCCCについて少し読んだことがありますが、まだよくわかりません。
私の質問は
上記の抽象化は、圏論に目を細める何らかの方法によって正当化されますか?
もしそうなら、クラスとそのインスタンスについて話すのに適切なCTに基づいた言語は何でしょうか?
編集:それが役に立ち、上記の単純化が物事を歪めている場合:私の実際のアプリケーションでは、ネストされた製品と副産物を扱ってい(1,(2,(3,())))
ます。これが実際のコードです (ただし、退屈な理由から、最後のインスタンスは簡略化されており、書かれているように単独では機能しません)
instance Uncurry () r where
type () :->-> r = r
uncurry = const
instance (Uncurry bs r)=> Uncurry (a,bs) r where
type (a,bs) :->-> r = a -> bs :->-> r
uncurry f = Prelude.uncurry (uncurry . f)
-- Not quite correct
instance (Uncurry bs c, Uncurry a c)=> Uncurry (Either a bs) c where
type Either a bs :->-> c = (a :->-> c, bs :->-> c)
uncurry (f,fs) = either (uncurry f) (uncurry fs) -- or as Sassa NF points out:
-- uncurry (|||)
したがって、const
インスタンスは()
、n-ary tuple uncurry インスタンスの再帰的な基本ケースとして自然に生まれましたが、3 つすべてを一緒に見ると... 恣意的なものではないように見えました。
アップデート
代数演算の観点から考えると、「ADT の代数」についてのChris Taylor のブログであることがわかりました。そうすることで、私のクラスとメソッドが実際には指数法則にすぎないことが明らかになりました (そして、私の最後のインスタンスが正しくなかった理由)。
結果は、私のshapely-data
パッケージのExponent
およびBase
クラスで確認できます。ノートと不安定でないドキュメント マークアップのソースも参照してください。