Haskell Language
Typ familjer
Sök…
Skriv synonymfamiljer
Typfamiljer är bara typnivåfunktioner: de kopplar parametertyper till resultattyper. Dessa finns i tre olika sorter.
Stängda typ-synonymfamiljer
Dessa fungerar mycket som vanliga Haskell-funktioner på vanligt värde: du anger några klausuler och mappar vissa typer till andra:
{-# LANGUAGE TypeFamilies #-}
type family Vanquisher a where
Vanquisher Rock = Paper
Vanquisher Paper = Scissors
Vanquisher Scissors = Rock
data Rock=Rock; data Paper=Paper; data Scissors=Scissors
Öppna typ-synonymfamiljer
Dessa fungerar mer som typkategoriinställningar: vem som helst kan lägga till fler klausuler i andra moduler.
type family DoubledSize w
type instance DoubledSize Word16 = Word32
type instance DoubledSize Word32 = Word64
-- Other instances might appear in other modules, but two instances cannot overlap
-- in a way that would produce different results.
Klassassocierade synonymer
En öppen familj kan också kombineras med en faktisk klass. Detta görs vanligtvis, liksom med tillhörande datafamiljer , kräver någon klassmetod ytterligare hjälpobjekt, och dessa hjälparobjekt kan vara olika för olika instanser men kanske också delas. Ett bra exempel är VectorSpace
klassen :
class VectorSpace v where
type Scalar v :: *
(*^) :: Scalar v -> v -> v
instance VectorSpace Double where
type Scalar Double = Double
μ *^ n = μ * n
instance VectorSpace (Double,Double) where
type Scalar (Double,Double) = Double
μ *^ (n,m) = (μ*n, μ*m)
instance VectorSpace (Complex Double) where
type Scalar (Complex Double) = Complex Double
μ *^ n = μ*n
Lägg märke till hur implementeringen av Scalar
i de två första fallen är densamma. Detta skulle inte vara möjligt med en tillhörande datafamilj: datafamiljer är injektiva , typfamiljer är det inte.
Även om icke-injektivitet öppnar upp vissa möjligheter som ovan, gör det också typinferens svårare. Till exempel kommer följande inte att kontrollera:
class Foo a where
type Bar a :: *
bar :: a -> Bar a
instance Foo Int where
type Bar Int = String
bar = show
instance Foo Double where
type Bar Double = Bool
bar = (>0)
main = putStrLn (bar 1)
I det här fallet kan kompilatorn inte veta vilken instans som ska användas, eftersom argumentet för att bar
är bara en polymorfisk bokstavlig Num
. Och typfunktionen Bar
kan inte lösas i "omvänd riktning", precis för att det inte är injicerande † och därmed inte inverterbar (det kan finnas mer än en typ med Bar a = String
).
† Med endast dessa två fall är det faktiskt injektiv, men kompilatorn kan inte känna någon kommer inte att lägga flera instanser senare och därmed bryta beteendet.
Datatypfamiljer
Datafamiljer kan användas för att bygga datatyper som har olika implementationer baserat på deras typargument.
Fristående datafamiljer
{-# LANGUAGE TypeFamilies #-}
data family List a
data instance List Char = Nil | Cons Char (List Char)
data instance List () = UnitList Int
I ovanstående deklaration, Nil :: List Char
, och UnitList :: Int -> List ()
Tillhörande datafamiljer
Datafamiljer kan också associeras med typglasögon. Detta är ofta användbart för typer med "hjälpobjekt", som krävs för generiska typklassmetoder men måste innehålla annan information beroende på den konkreta instansen. Till exempel kräver indexering av platser i en lista bara ett enda nummer, medan du i ett träd behöver ett nummer för att ange sökvägen vid varje nod:
class Container f where
data Location f
get :: Location f -> f a -> Maybe a
instance Container [] where
data Location [] = ListLoc Int
get (ListLoc i) xs
| i < length xs = Just $ xs!!i
| otherwise = Nothing
instance Container Tree where
data Location Tree = ThisNode | NodePath Int (Location Tree)
get ThisNode (Node x _) = Just x
get (NodePath i path) (Node _ sfo) = get path =<< get i sfo
injektivitet
Typfamiljer är inte nödvändigtvis injicerande. Därför kan vi inte sluta parametern från en applikation. Till exempel, i servant
, med tanke på en typ Server a
vi inte dra slutsatsen a
. För att lösa detta problem kan vi använda Proxy
. Till exempel i servant
har serve
typen ... Proxy a -> Server a -> ...
Vi kan härleda a
från Proxy a
eftersom Proxy
definieras av data
som är injektionsmedel.