亞歷克表示,bimap
有利於對。如果你想更一般地處理元組,一種選擇是使用一個類。下面的靈感來自於lens
(特別是Control.Lens.Tuple
)的一些想法,但我不認爲它與其中的任何想法完全相同。
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Tuplish where
import Data.Functor.Identity (Identity (..))
import Data.Profunctor.Unsafe ((#.), (.#))
class Tuply s t a b | s -> a, t -> b, s b -> t, t a -> s where
ttraverse :: Applicative f => (a -> f b) -> s -> f t
mapTuple :: Tuply s t a b => (a -> b) -> (s -> t)
mapTuple = (runIdentity .) #. ttraverse .# (Identity .)
instance (a1 ~ a, a2 ~ a, b1 ~ b, b2 ~ b)
=> Tuply (a1, a2) (b1, b2) a b where
ttraverse f (x,y) = (,) <$> f x <*> f y
instance (a1 ~ a, a2 ~ a, a3 ~ a, b1 ~ b, b2 ~ b, b3 ~ b)
=> Tuply (a1,a2,a3) (b1,b2,b3) a b where
ttraverse f (x,y,z) = (,,) <$> f x <*> f y <*> f z
instance (a1 ~ a, a2 ~ a, a3 ~ a, a4 ~ a, b1 ~ b, b2 ~ b, b3 ~ b, b4 ~ b)
=> Tuply (a1,a2,a3,a4) (b1,b2,b3,b4) a b where
ttraverse f (x,y,z,w) = (,,,) <$> f x <*> f y <*> f z <*> f w
instance (a1 ~ a, a2 ~ a, a3 ~ a, a4 ~ a, a5 ~ a,
b1 ~ b, b2 ~ b, b3 ~ b, b4 ~ b, b5 ~ b)
=> Tuply (a1,a2,a3,a4,a5) (b1,b2,b3,b4,b5) a b where
ttraverse f (x,y,z,w,u) = (,,,,) <$> f x <*> f y <*> f z <*> f w <*> f u
instance (a1 ~ a, a2 ~ a, a3 ~ a, a4 ~ a, a5 ~ a, a6 ~ a,
b1 ~ b, b2 ~ b, b3 ~ b, b4 ~ b, b5 ~ b, b6 ~ b)
=> Tuply (a1,a2,a3,a4,a5,a6) (b1,b2,b3,b4,b5,b6) a b where
ttraverse f (x,y,z,w,u,v) = (,,,,,) <$> f x <*> f y <*> f z <*> f w <*> f u <*> f v
「奇怪!」 - 它實際上是唯一可能的實現,但是在數據構造函數上使用的元組類型的語法隱藏了它。更直接的將是:'數據元組a b =元組a b'; '實例Functor(Tuple a)'...所以元組上'fmap'的具體類型簽名是(爲了清晰起見,有冗餘的元素):'fmap ::(b - > c) - >(Tuple a)b - > (元組a)c'其中'元組a'是你的'f'。 – jberryman
當我第一次遇到它時,我將驗證這個「最後的類型參數」業務確實很奇怪。 Haskell對參數的順序有些敏感,特別是類型參數,這與基本上所有其他語言並不重要的語言完全不同。 – luqui