実際には、次のものが必要ですFunctor
。
listUncurry :: Functor f => (a -> a -> a -> r) -> f [a] -> f r
listUncurry h p =
(\[x, y, z] -> h x y z) <$> p
私にとっては、次のFunctor
ようなコード パターンがある場合にのみ必要なヒントです。
do x <- m
return (f ...)
これは、
m >>= (\x -> return (f ...))
これはと同じです
fmap (\x -> f ...) m
これは、モナドの法則が次の同一性を暗示しているためです。
fmap f xs = xs >>= return . f
多変量listUncurry
コンパイル時エラーが実行時エラーに変わるため、ほとんどの状況ではこれをお勧めしませんが、これは polyvariadic を実装する方法ですlistUncurry
:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
class ListUncurry a x r where
listUncurry :: a -> [x] -> r
instance ListUncurry k a r => ListUncurry (a -> k) a r where
listUncurry f (x:xs) = listUncurry (f x) xs
listUncurry _ _ = error "listUncurry: Too few arguments given"
instance ListUncurry r a r where
listUncurry r [] = r
listUncurry _ _ = error "listUncurry: Too many arguments given"
それも使用する場合は、多くの明示的な型注釈が必要になります。それを助けるために型ファミリーまたは関数の依存関係を使用する方法があるかもしれませんが、現時点では頭の中でそれを考えることができません. それはおそらく(少なくともある程度は)解決可能であるため、私の考えでは、より大きな問題は、型エラーがコンパイル時エラーから実行時エラーに変更されることです。
使用例:
ghci> listUncurry ord ['a'] :: Int
97
ghci> listUncurry ((==) :: Int -> Int -> Bool) [1,5::Int] :: Bool
False
ghci> listUncurry ((==) :: Char -> Char -> Bool) ['a'] :: Bool
*** Exception: listUncurry: Too few arguments given
ghci> listUncurry ((==) :: Char -> Char -> Bool) ['a','b','c'] :: Bool
*** Exception: listUncurry: Too many arguments given
より安全listUncurry
クラスを変更すると
class ListUncurry a x r where
listUncurry :: a -> [x] -> Maybe r
インスタンスのエラーケースを適切に変更すると、少なくともエラーを処理するためのより良いインターフェイスが得られます。Maybe
その情報を保持したい場合は、「多すぎる」引数エラーと「少なすぎる」引数エラーを区別する型に を置き換えることもできます。
もう少しエラー処理を追加する必要がありますが、これはアプローチとしては少し良いと思います (とインターフェイスMaybe
はこれをかなりうまくします)。Functor
Applicative
Monad
2 つのアプローチの比較
最終的には、これが表すエラーの種類によって異なります。このようなエラーが発生した場合、プログラムの実行が意味のある方法で続行できなくなる場合は、最初のアプローチ (またはそれに似たもの) が 2 番目のアプローチよりも適切である可能性があります。エラーから回復する方法がある場合は、最初の方法よりも 2 番目の方法の方が適しています。
そもそも多変量法を使用する必要があるかどうかは別の問題です。多変量の追加の複雑さを避けるために、プログラムを再構築する方が良いかもしれません。