Sök…


Introduktion

Typglasögon i Haskell är ett sätt att definiera beteendet förknippat med en typ separat från den typens definition. Medan säga, i Java, skulle du definiera beteendet som en del av typens definition - dvs i ett gränssnitt, abstrakt klass eller konkret klass - Haskell håller dessa två saker åtskilda.

Det finns ett antal typeclasses redan definierade i Haskell s base paket. Förhållandet mellan dessa illustreras i avsnittet Kommentarer nedan.

Anmärkningar

Följande diagram som tagits från artikeln Typeclassopedia visar förhållandet mellan de olika typglasögonen i Haskell.

Relationer mellan vanliga klasser av Haskell-typ, figur 1 som publicerades i Typeclassopedia.

Kanske och Functor Class

I Haskell kan datatyper ha argument precis som funktioner. Ta till exempel Maybe typen.

Maybe är en mycket användbar typ som gör att vi kan representera idén om misslyckande eller deras möjlighet. Med andra ord, om det finns en möjlighet att en beräkning misslyckas använder vi typen Maybe där. Maybe fungerar som ett omslag för andra typer, vilket ger dem ytterligare funktionalitet.

Dess verkliga förklaring är ganska enkel.

Maybe a = Just a | Nothing

Vad detta berättar är att ett Maybe finns i två former, en Just , som representerar framgång och ett Nothing , som representerar misslyckande. Just ett argument som bestämmer typen av Maybe , och Nothing tar inget. Till exempel kommer värdet Just "foo" att ha typen Maybe String , som är en strängtyp som är lindad med den extra Maybe funktionen. Värdet Nothing har typ Maybe a där a kan vara vilken typ som helst.

Denna idé om inslagningstyper för att ge dem ytterligare funktionalitet är en mycket användbar idé och är tillämplig på mer än bara Maybe . Andra exempel inkluderar Either , Either IO och listtyper, var och en med olika funktioner. Det finns dock vissa åtgärder och förmågor som är gemensamma för alla dessa omslagstyper. Det mest anmärkningsvärda av dessa är förmågan att ändra det inkapslade värdet.

Det är vanligt att tänka på denna typ av typer som rutor som kan ha värden placerade i dem. Olika rutor har olika värden och gör olika saker, men ingen är användbar utan att kunna komma åt innehållet inom.

För att kapsla in denna idé kommer Haskell med ett standardtypklass, med namnet Functor . Det definieras enligt följande.

class Functor f where
  fmap :: (a -> b) -> f a -> f b

Som framgår har klassen en enda funktion, fmap , av två argument. Det första argumentet är en funktion från en typ, a , till en annan, b . Det andra argumentet är en funktor (omslagstyp) som innehåller ett värde av typ a . Den returnerar en funktor (omslagstyp) som innehåller ett värde av typ b .

Enkelt fmap tar fmap en funktion och gäller värdet inuti en funktor. Det är den enda funktion som krävs för att en typ ska vara medlem i Functor klassen, men den är oerhört användbar. Funktioner som fungerar på funktorer som har mer specifika applikationer kan hittas i Applicative och Monad typglasögon.

Typklassarv: Ord typklass

Haskell stöder en uppfattning om klassförlängning. Exempelvis ärver klassen Ord alla operationer i Eq , men har dessutom en compare funktion som returnerar en Ordering mellan värden. Ord kan också innehålla operatörer för vanliga ordningsjämförelser, såväl som en min metod och en max metod.

Notationen => har samma betydelse som den gör i en funktionssignatur och kräver typ a att implementera Eq för att implementera Ord .

data Ordering = EQ | LT | GT

class Eq a => Ord a where
    compare :: Ord a => a -> a -> Ordering
    (<)     :: Ord a => a -> a -> Bool
    (<=)    :: Ord a => a -> a -> Bool
    (>)     :: Ord a => a -> a -> Bool
    (>=)    :: Ord a => a -> a -> Bool
    min     :: Ord a => a -> a -> a
    max     :: Ord a => a -> a -> a

Alla metoder som följer compare kan härledas från det på ett antal sätt:

x < y   = compare x y == LT
x <= y  = x < y || x == y -- Note the use of (==) inherited from Eq
x > y   = not (x <= y)
x >= y  = not (x < y)

min x y = case compare x y of
               EQ -> x
               LT -> x
               GT -> y

max x y = case compare x y of
               EQ -> x
               LT -> y
               GT -> x

Typklasser som själva utvidgar Ord måste implementera åtminstone antingen compare eller (<=) metoden själva, som bygger upp det riktade arvgitteret.

ekv

Alla grundläggande datatyper (som Int , String , Eq a => [a] ) från Prelude förutom för funktioner och IO har instanser av Eq . Om en typ instanser Eq betyder det att vi vet hur man kan jämföra två värden för värde eller strukturell jämlikhet.

> 3 == 2 
False
> 3 == 3
True

Obligatoriska metoder

  • (==) :: Eq a => a -> a -> Boolean eller (/=) :: Eq a => a -> a -> Boolean (om bara en är implementerad, är den andra som standard att negationen av definierad en)

definierar

  • (==) :: Eq a => a -> a -> Boolean
  • (/=) :: Eq a => a -> a -> Boolean

Direkt superklass

Ingen

Anmärkningsvärda underklasser

Ord

Typer som instanterar Ord inkluderar t.ex. Int , String och [a] (för typer a där det finns en Ord a instans). Om en typ instanserar Ord betyder det att vi känner till en "naturlig" ordning av värden av den typen. Observera att det ofta finns många möjliga val av den "naturliga" beställningen av en typ och Ord tvingar oss att gynna en.

Ord tillhandahåller operatörerna för standard (<=) , (<) , (>) , (>=) men definierar intressant dem alla med en anpassad algebraisk datatyp

data Ordering = LT | EQ | GT

compare :: Ord a => a -> a -> Ordering

Obligatoriska metoder

  • compare :: Ord a => a -> a -> Ordering eller (<=) :: Ord a => a -> a -> Boolean (standardens standard compare metod användningar (<=) i dess genomförande)

definierar

  • compare :: Ord a => a -> a -> Ordering
  • (<=) :: Ord a => a -> a -> Boolean
  • (<) :: Ord a => a -> a -> Boolean
  • (>=) :: Ord a => a -> a -> Boolean
  • (>) :: Ord a => a -> a -> Boolean
  • min :: Ord a => a -> a -> a
  • max :: Ord a => a -> a -> a

Direkt superklass

monoid

Typer som instabiliserar Monoid inkluderar listor, nummer och funktioner med Monoid returvärden, bland andra. För att installa Monoid måste en typ stödja en associativ binär operation ( mappend eller (<>) ) som kombinerar dess värden och har ett speciellt "noll" mempty ( mempty ) så att en kombination av ett värde inte ändrar det värdet:

mempty  <>  x == x
x <>  mempty  == x

x <> (y <> z) == (x <> y) <> z

Intuitivt är Monoid typer "listliknande" genom att de stödjer bifogade värden tillsammans. Alternativt kan Monoid betraktas som sekvenser av värden för vilka vi bryr oss om ordningen men inte gruppering. Till exempel är ett binärt träd en Monoid , men med hjälp av Monoid operationerna kan vi inte bevittna dess grenstruktur, bara en genomgång av dess värden (se Foldable och Traversable ).

Obligatoriska metoder

  • mempty :: Monoid m => m
  • mappend :: Monoid m => m -> m -> m

Direkt superklass

Ingen

Num

Den mest allmänna klassen för nummertyper, mer exakt för ringar , dvs siffror som kan läggas till och subtraheras och multipliceras i vanlig mening, men inte nödvändigtvis delas.

Denna klass innehåller både integrerade typer ( Int , Integer , Word32 etc.) och bråkstyper ( Double , Rational , också komplexa siffror etc.). Vid finita typer förstås semantiken generellt som modulär aritmetik , dvs med över- och underflöde .

Observera att reglerna för de numeriska klasserna följs mycket mindre strikt än monad- eller monoidlagarna, eller reglerna för jämställdhetsjämförelse . I synnerhet följer antalet flytande punkter generellt bara lagar i ungefärlig mening.

Metoderna

  • fromInteger :: Num a => Integer -> a . konvertera ett heltal till den allmänna siffertypen (om nödvändigt). Haskell- antalet bokstäver kan förstås som ett monomorfalt Integer bokstavligt med den allmänna konverteringen runt, så att du kan använda bokstäverna 5 i både ett Int sammanhang och en Complex Double dubbelinställning.

  • (+) :: Num a => a -> a -> a . Standardtillägg, allmänt förstått som associerande och kommutativ, dvs.

      a + (b + c) ≡ (a + b) + c
      a + b ≡ b + a
    
  • (-) :: Num a => a -> a -> a . Subtraktion, som är det inversa av tillägg:

      (a - b) + b ≡ (a + b) - b ≡ a
    
  • (*) :: Num a => a -> a -> a . Multiplikation, en associerande operation som är fördelaktig över tillägg:

      a * (b * c) ≡ (a * b) * c
      a * (b + c) ≡ a * b + a * c
    

    för de vanligaste fallen är multiplikation också kommutativ, men detta är definitivt inte ett krav.

  • negate :: Num a => a -> a . Det fullständiga namnet på operatören med unary negation. -1 är syntaktiskt socker för negate 1 .

      -a ≡ negate a ≡ 0 - a
    
  • abs :: Num a => a -> a . Funktionen absolutvärde ger alltid ett icke-negativt resultat av samma storlek

      abs (-a) ≡ abs a
      abs (abs a) ≡ abs a
    

    abs a ≡ 0 bör bara hända om a ≡ 0 .

    För verkliga typer är det tydligt vad icke-negativt betyder: du har alltid abs a >= 0 . Komplexa etc.-typer har inte en väldefinierad beställning, men resultatet av abs bör alltid ligga i den verkliga underuppsättningen (dvs ge ett nummer som också kan skrivas som ett enda bokstavligt bokstav utan negation).

  • signum :: Num a => a -> a . Teckenfunktionen ger enligt namnet endast -1 eller 1 , beroende på argumentets tecken. Egentligen är det bara sant för icke-andra verkliga siffror; i allmänhet förstås signum bättre som normaliseringsfunktionen :

      abs (signum a) ≡ 1   -- unless a≡0
      signum a * abs a ≡ a -- This is required to be true for all Num instances
    

    Observera att avsnitt 6.4.4 i Haskell 2010-rapporten uttryckligen kräver att denna sista jämlikhet ska gälla för alla giltiga Num instanser.


Vissa bibliotek, särskilt linjär och hmatrix , har en mycket slappare förståelse för vad Num klassen är för: de behandlar det bara som ett sätt att överbelasta de aritmetiska operatörerna . Även om detta är ganska enkelt för + och - , blir det redan besvärligt med * och mer så med de andra metoderna. Bör * innebära matrismultiplikation eller elementmässig multiplikation?
Det är utan tvekan en dålig idé att definiera sådana fall som inte är nummer. tänk på dedikerade klasser som VectorSpace .


I synnerhet lindas "negativerna" av osignerade typer till stora positiva, t.ex. (-4 :: Word32) == 4294967292 .

Detta uppfylls ofta inte : vektortyper har inte en riktig delmängd. De kontroversiella Num inställningarna för sådana typer definierar i allmänhet abs och signum elementmässigt, vilket matematiskt sett inte riktigt är vettigt.



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