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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow