Recherche…


Syntaxe de base

Les enregistrements sont une extension du type de data algébrique somme qui permet de nommer les champs:

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
  }

Les noms de champs peuvent alors être utilisés pour extraire le champ nommé

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

Les enregistrements peuvent être associés à un motif

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

Notez que tous les champs ne doivent pas être nommés

Les enregistrements sont créés en nommant leurs champs, mais peuvent également être créés en tant que types de somme ordinaires (souvent utiles lorsque le nombre de champs est petit et peu susceptible de changer)

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

Si un enregistrement est créé sans champ nommé, le compilateur émettra un avertissement et la valeur résultante sera undefined .

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

Un champ d'un enregistrement peut être mis à jour en définissant sa valeur. Les champs non mentionnés ne changent pas.

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

Il est souvent utile de créer des objectifs pour des types d’enregistrements complexes.

Copier des enregistrements en changeant les valeurs de champs

Supposons que vous ayez ce type:

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

et deux valeurs:

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

Une nouvelle valeur de type Person peut être créée en copiant depuis alex , en spécifiant les valeurs à modifier:

anotherAlex = alex { age = 31 }

Les valeurs de alex et anotherAlex seront maintenant:

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

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

Records avec newtype

La syntaxe d'enregistrement peut être utilisée avec newtype avec la restriction qu'il existe exactement un constructeur avec un seul champ. L'avantage ici est la création automatique d'une fonction pour dérouler le nouveau type. Ces champs sont souvent nommés en commençant par run pour les monads, get pour les monoids et un pour les autres types.

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

Il est important de noter que la syntaxe de l’enregistrement n’est généralement pas utilisée pour former des valeurs et que le nom du champ est strictement utilisé pour le dépliage.

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

Le modèle Client{..} apporte la portée à tous les champs du constructeur Client , et est équivalent au pattern

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

Il peut également être combiné avec d'autres agents de terrain, comme ceci:

Client { firstName = "Joe", .. }

Ceci est équivalent à

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

Définition d'un type de données avec des étiquettes de champ

Il est possible de définir un type de données avec des étiquettes de champ.

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

Cette définition diffère d'une définition d'enregistrement normale car elle définit également * les accesseurs d'enregistrement qui peuvent être utilisés pour accéder à des parties d'un type de données.

Dans cet exemple, deux accesseurs d’enregistrement sont définis: age et name , ce qui nous permet d’accéder respectivement aux champs age et name .

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

Les accesseurs d'enregistrements ne sont que des fonctions Haskell générées automatiquement par le compilateur. En tant que tels, ils sont utilisés comme des fonctions Haskell ordinaires.

En nommant les champs, nous pouvons également utiliser les étiquettes de champs dans plusieurs autres contextes afin de rendre notre code plus lisible.

Correspondance de motif

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

Nous pouvons lier la valeur située à la position du libellé du champ concerné tout en faisant correspondre le modèle à une nouvelle valeur (dans ce cas x ) qui peut être utilisée sur le RHS d'une définition.

Correspondance de modèle avec NamedFieldPuns

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

L'extension NamedFieldPuns nous permet à la place de simplement spécifier le libellé du champ sur lequel nous voulons faire correspondre, ce nom est alors ombré sur le RHS d'une définition, ainsi la référence au name fait référence à la valeur plutôt qu'à l'accesseur d'enregistrement.

Correspondance de motifs avec RecordWildcards

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

Lors de la correspondance à l'aide de RecordWildCards , toutes les étiquettes de champs sont intégrées à la portée. (Dans cet exemple spécifique, name et age )

Cette extension est légèrement controversée car on ne sait pas comment les valeurs sont intégrées si vous n'êtes pas certain de la définition de Person .

Mises à jour des enregistrements

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

Il existe également une syntaxe spéciale pour la mise à jour des types de données avec des étiquettes de champ.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow