Zoeken…


Basissyntaxis

Records zijn een uitbreiding van het som algebraïsche data waarmee velden de naam kunnen krijgen:

data StandardType = StandardType String Int Bool --standard way to create a sum type

data RecordType = RecordType { -- the same sum type with record syntax
    aString :: String
  , aNumber :: Int
  , isTrue  :: Bool
  }

De veldnamen kunnen vervolgens worden gebruikt om het genoemde veld uit het record te halen

> let r = RecordType {aString = "Foobar", aNumber= 42, isTrue = True}
> :t r
  r :: RecordType
> :t aString
  aString :: RecordType -> String
> aString r
  "Foobar"

Records kunnen worden vergeleken met een patroon

case r of
  RecordType{aNumber = x, aString=str} -> ... -- x = 42, str = "Foobar"

Merk op dat niet alle velden moeten worden genoemd

Records worden gemaakt door hun velden een naam te geven, maar kunnen ook worden gemaakt als gewone somtypen (vaak handig als het aantal velden klein is en waarschijnlijk niet zal veranderen)

r  = RecordType {aString = "Foobar", aNumber= 42, isTrue = True}
r' = RecordType  "Foobar" 42 True

Als een record wordt gemaakt zonder een benoemd veld, geeft de compiler een waarschuwing en is de resulterende waarde undefined .

> let r = RecordType {aString = "Foobar", aNumber= 42}
  <interactive>:1:9: Warning:
     Fields of RecordType not initialized: isTrue
> isTrue r
  Error 'undefined'

Een veld van een record kan worden bijgewerkt door de waarde ervan in te stellen. Niet vermelde velden veranderen niet.

> let r = RecordType {aString = "Foobar", aNumber= 42, isTrue = True}
> let r' = r{aNumber=117}
    -- r'{aString = "Foobar", aNumber= 117, isTrue = True}

Het is vaak handig om lenzen te maken voor gecompliceerde recordtypen.

Records kopiëren terwijl veldwaarden worden gewijzigd

Stel dat je dit type hebt:

data Person = Person { name :: String, age:: Int } deriving (Show, Eq)

en twee waarden:

alex = Person { name = "Alex", age = 21 }
jenny = Person { name = "Jenny", age = 36 }

een nieuwe waarde van het type Person kan worden gemaakt door te kopiëren van alex en op te geven welke waarden moeten worden gewijzigd:

anotherAlex = alex { age = 31 }

De waarden van alex en anotherAlex zijn nu:

Person {name = "Alex", age = 21}

Person {name = "Alex", age = 31}

Records met nieuw type

newtype kan worden gebruikt met newtype met de beperking dat er precies één constructor is met precies één veld. Het voordeel hiervan is dat er automatisch een functie wordt gemaakt om het nieuwe type uit te pakken. Deze velden worden vaak genoemd beginnend met run voor monaden, get voor monoiden en un voor andere typen.

newtype State s a = State { runState :: s -> (s, a) }

newtype Product a = Product { getProduct :: a }

newtype Fancy = Fancy { unfancy :: String } 
  -- a fancy string that wants to avoid concatenation with ordinary strings

Het is belangrijk op te merken dat de syntaxis van de record meestal nooit wordt gebruikt om waarden te vormen en dat de veldnaam strikt wordt gebruikt voor het uitpakken

getProduct $ mconcat [Product 7, Product 9, Product 12]
-- > 756

RecordWildCards

{-# LANGUAGE RecordWildCards #-}

data Client = Client { firstName     :: String
                     , lastName      :: String
                     , clientID      :: String 
                     } deriving (Show)

printClientName :: Client -> IO ()
printClientName Client{..} = do
    putStrLn firstName
    putStrLn lastName
    putStrLn clientID

Het patroon Client{..} alle velden van de constructor Client en is gelijk aan het patroon

Client{ firstName = firstName, lastName = lastName, clientID = clientID }

Het kan ook worden gecombineerd met andere veldmatchers zoals:

Client { firstName = "Joe", .. }

Dit komt overeen met

Client{ firstName = "Joe", lastName = lastName, clientID = clientID }

Een gegevenstype definiëren met veldlabels

Het is mogelijk om een gegevenstype met veldlabels te definiëren.

data Person = Person { age :: Int, name :: String }

Deze definitie verschilt van een normale recorddefinitie, aangezien deze ook * recordtoegangsmiddelen definieert die kunnen worden gebruikt om toegang te krijgen tot delen van een gegevenstype.

In dit voorbeeld zijn twee opname accessors gedefinieerd, age en name , die ons in staat om toegang te krijgen tot de age en name respectievelijk gebied.

age :: Person -> Int
name :: Person -> String

Record accessors zijn slechts Haskell-functies die automatisch door de compiler worden gegenereerd. Als zodanig worden ze gebruikt als gewone Haskell-functies.

Door velden een naam te geven, kunnen we de veldlabels ook in een aantal andere contexten gebruiken om onze code leesbaarder te maken.

Patroon matching

lowerCaseName :: Person -> String
lowerCaseName (Person { name = x }) = map toLower x

We kunnen de waarde op de positie van het relevante veldlabel binden terwijl het patroon overeenkomt met een nieuwe waarde (in dit geval x ) die op de RHS van een definitie kan worden gebruikt.

Patroonaanpassing met NamedFieldPuns

lowerCaseName :: Person -> String
lowerCaseName (Person { name }) = map toLower name

Met de extensie NamedFieldPuns kunnen we in plaats daarvan alleen het NamedFieldPuns specificeren NamedFieldPuns we willen matchen, deze naam wordt vervolgens in de schaduw van de RHS van een definitie geplaatst, dus verwijzing naar name verwijst naar de waarde in plaats van naar de recordtoegang.

Patroonovereenkomst met RecordWildcards

lowerCaseName :: Person -> String
lowerCaseName (Person { .. }) = map toLower name

Bij het matchen met RecordWildCards worden alle RecordWildCards binnen bereik gebracht. (In dit specifieke voorbeeld, name en age )

Deze extensie is enigszins controversieel omdat het niet duidelijk is hoe waarden binnen het bereik worden gebracht als u niet zeker bent van de definitie van Person .

Updates opnemen

setName :: String -> Person -> Person
setName newName person = person { name = newName }

Er is ook een speciale syntaxis voor het bijwerken van gegevenstypen met veldlabels.



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