HaskellのFunctorで簡単な複素数足し算プログラム
前回の記事(HaskellでFunctorを使って簡単な複素数の足し算プログラム)では、あまりうまくできず、無理矢理なプログラムになりました。
今回は、Applicativeも使ってみました。
ただ、既存のライブラリのFunctorやApllicativeでは、どうしてもできなかったため(方法はあるか?)、その二つのクラスから定義しました。
また、前回の記事でコメントを頂いて、それを参考にさせて頂きました。ありがとうございます。
プログラム
complex.hs
data ComplexNumber n = Z n n
class ComplexFunctor t where
cfmap :: (a -> b) -> (a -> b) -> t a -> t b
class (ComplexFunctor c) => ComplexApplicative c where
cpure :: a -> a -> c a
(<**>) :: c(a -> b) -> c a -> c b
instance ComplexFunctor ComplexNumber where
cfmap func1 func2 (Z a b) = Z (func1 a) (func2 b)
instance ComplexApplicative ComplexNumber where
cpure a b = Z a b
(Z func1 func2) <**> c = cfmap func1 func2 c
main::IO()
main = do
let ans = cpure (+) (+) <**> Z 1 2 <**> Z 3 4
let a = (\(Z a b) -> (a,b) ) ans
print a
解説
data ComplexNumber n = Z n n
Z n n
は、実部と虚部をそれぞれ組み合わせた複素数を表しています。
class ComplexFunctor t where
cfmap :: (a -> b) -> (a -> b) -> t a -> t b
class (ComplexFunctor c) => ComplexApplicative c where
cpure :: a -> a -> c a
(<**>) :: c(a -> b) -> c a -> c b
いろいろと試しましたが、標準ライブラリのFunctorやApplicativeではできなくて、ここから自作することにしました。
instance ComplexFunctor ComplexNumber where
cfmap func1 func2 (Z a b) = Z (func1 a) (func2 b)
instance ComplexApplicative ComplexNumber where
cpure a b = Z a b
(Z func1 func2) <**> c = cfmap func1 func2 c
インスタンスです。よく見る形をちょっと変えたものです。
let ans = cpure (+) (+) <**> Z 1 2 <**> Z 3 4
main
関数の実際に複素数の足し算をしているプログラムです。
これもよく見る形をちょっと変えたものです。
分かったこと
今回分かったことは、Haskellは難解だけど、楽しそうだと言うことです。
標準FunctorやApplicativeではできず、自作してしまいましたが、本当に標準でできないかは、もう少し考えて見る必要がありそうです。
main
関数の
let ans = cpure (+) (+) <**> Z 1 2 <**> Z 3 4
の部分で、
let ans = cpure (+) (+) <**> Z 1 2 <**> Z 3 4 <**> Z 5 6
はエラーになってしまいます。Z 3 4
の計算した時点で、3つ目の<**>
の左辺が関数ではなくなるからです。
pure f <*> Just x <*> Just y <*> Just z <*>....
あるHaskell解説書では、3つ以上<*>
が繋げられるようなことも書いてあります。
f
が、それだけ引数を持っているということですね。