Zoeken…


Type Synoniem Gezinnen

Type synoniemenfamilies zijn slechts functies op type niveau: ze koppelen parametertypen aan resultatietypen. Deze zijn er in drie verschillende varianten.

Gesloten type-synoniemfamilies

Deze werken op dezelfde manier als gewone Haskell-functies op waardeniveau: u geeft enkele clausules op, waarbij bepaalde typen aan andere worden toegewezen:

{-# 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

Open type-synoniemenfamilies

Deze werken meer als voorbeelden van typeclass: iedereen kan meer clausules toevoegen in andere modules.

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.

Klasse-geassocieerde type synoniemen

Een open type familie kan ook worden gecombineerd met een echte klasse. Dit wordt meestal gedaan wanneer, net als met de bijbehorende gegevens families , wat klasse methode moet extra helper objecten, en deze helper objecten kan verschillend zijn voor verschillende instanties, maar kan eventueel ook gedeeld. Een goed voorbeeld is de VectorSpace klasse :

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

Merk op dat in de eerste twee gevallen de implementatie van Scalar hetzelfde is. Dit zou niet mogelijk zijn met een bijbehorende gegevensfamilie: gegevensfamilies zijn injectief , type-synoniemenfamilies niet.

Hoewel niet-injectiviteit sommige mogelijkheden zoals de bovenstaande opent, maakt het ook type-inferentie moeilijker. Het volgende zal bijvoorbeeld geen typecontrole uitvoeren:

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)

In dit geval kan de compiler niet weten welke instantie hij moet gebruiken, omdat het argument om te bar zelf slechts een polymorf Num letterlijk is. En de typefunctie Bar kan niet in "omgekeerde richting" worden opgelost, juist omdat het niet injectief is en dus niet omkeerbaar is (er kan meer dan één type zijn met Bar a = String ).


Met slechts deze twee gevallen is het eigenlijk injectief, maar de compiler kan niet weten zal iemand niet meer gevallen toe te voegen later op en daarmee het gedrag te doorbreken.

Datatype Families

Datafamilies kunnen worden gebruikt om datatypes te bouwen die verschillende implementaties hebben op basis van hun typeargumenten.

Standalone gegevensfamilies

{-# LANGUAGE TypeFamilies #-}
data family List a
data instance List Char = Nil | Cons Char (List Char)
data instance List () = UnitList Int

In de bovenstaande verklaring, Nil :: List Char en UnitList :: Int -> List ()

Bijbehorende datafamilies

Datafamilies kunnen ook worden geassocieerd met typeclasses. Dit is vaak handig voor typen met "helperobjecten", die vereist zijn voor generieke typeclass-methoden, maar die verschillende informatie moeten bevatten, afhankelijk van de concrete instantie. Voor het indexeren van locaties in een lijst is bijvoorbeeld slechts één nummer vereist, terwijl u in een structuur een nummer nodig hebt om het pad bij elk knooppunt aan te geven:

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

injectiviteit

Type Families zijn niet noodzakelijk injectief. Daarom kunnen we de parameter niet afleiden uit een toepassing. In servant kunnen we bijvoorbeeld een type Server a niet afleiden uit het type a . Om dit probleem op te lossen, kunnen we Proxy . In servant heeft de serve functie bijvoorbeeld het type ... Proxy a -> Server a -> ... We kunnen afleiden a van Proxy a omdat Proxy wordt bepaald door data die injectief.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow