Ricerca…


Sintassi di base

I record sono un'estensione del tipo di data algebrici di somma che consente di denominare i campi:

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
  }

I nomi dei campi possono quindi essere utilizzati per ottenere il campo denominato fuori dal record

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

I record possono essere abbinati a modelli

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

Si noti che non tutti i campi devono essere nominati

I record vengono creati nominando i loro campi, ma possono anche essere creati come tipi di somma ordinari (spesso utili quando il numero di campi è piccolo e non è probabile che cambi)

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

Se un record viene creato senza un campo denominato, il compilatore emetterà un avviso e il valore risultante sarà undefined .

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

Un campo di un record può essere aggiornato impostando il suo valore. I campi non menzionati non cambiano.

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

È spesso utile creare obiettivi per tipi di record complicati.

Copia dei record durante la modifica dei valori dei campi

Supponiamo di avere questo tipo:

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

e due valori:

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

un nuovo valore di tipo Person può essere creato copiando da alex , specificando quali valori modificare:

anotherAlex = alex { age = 31 }

I valori di alex e anotherAlex saranno ora:

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

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

Registra con newtype

La sintassi del record può essere utilizzata con newtype con la restrizione che esista esattamente un costruttore con esattamente un campo. Il vantaggio qui è la creazione automatica di una funzione per scartare il newtype. Questi campi sono spesso denominati che iniziano con run per monade, get per monoidi e un per altri tipi.

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

È importante notare che la sintassi del record in genere non viene mai utilizzata per formare valori e il nome del campo viene utilizzato esclusivamente per lo unwrapping

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

Il pattern Client{..} include tutti i campi del costruttore Client ed è equivalente al modello

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

Può anche essere combinato con altri field matcher in questo modo:

Client { firstName = "Joe", .. }

Questo è equivalente a

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

Definizione di un tipo di dati con etichette di campo

È possibile definire un tipo di dati con etichette di campo.

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

Questa definizione differisce da una normale definizione di record in quanto definisce anche * accessors di record che possono essere utilizzati per accedere a parti di un tipo di dati.

In questo esempio, sono definiti due accessor di record, age e name , che ci consentono di accedere rispettivamente ai campi age e name .

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

Gli accessor di registrazione sono solo funzioni Haskell che vengono generate automaticamente dal compilatore. In quanto tali, vengono utilizzati come normali funzioni Haskell.

Con i nomi dei campi, possiamo anche usare le etichette di campo in una serie di altri contesti per rendere il nostro codice più leggibile.

Pattern Matching

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

Possiamo associare il valore situato nella posizione dell'etichetta di campo pertinente mentre il pattern si abbina a un nuovo valore (in questo caso x ) che può essere utilizzato sull'RHS di una definizione.

Pattern Matching con NamedFieldPuns

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

L'estensione NamedFieldPuns invece ci consente semplicemente di specificare l'etichetta del campo su cui vogliamo far corrispondere, questo nome viene quindi ombreggiato sul RHS di una definizione, quindi il riferimento al name riferisce al valore piuttosto che all'accessorio del record.

Pattern Matching con RecordWildcards

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

Quando si RecordWildCards corrispondenza utilizzando RecordWildCards , tutte le etichette dei campi vengono portate nell'ambito. (In questo esempio specifico, name ed age )

Questa estensione è leggermente controverso in quanto non è chiaro come i valori siano portati in ambito se non si è sicuri della definizione di Person .

Registra aggiornamenti

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

C'è anche una sintassi speciale per l'aggiornamento dei tipi di dati con le etichette di campo.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow