Reputation: 909
I am trying to add a new element of type Transaction as defined:
--Build type Transaction--
data Transaction = Transaction {
start_state :: Int,
symbol :: Char,
end_state :: Int
} deriving Show
to the list of transactions which is part of the following type:
--Build type Automaton--
data Automaton = Automaton {
initial_state :: Int,
states :: Set.Set Int,
transactions :: [Transaction],
final_states :: Set.Set Int
} deriving Show
I have tried to implement the following function:
--Add to the automaton a transaction
insert_transaction :: Transaction -> Automaton -> Automaton
insert_transaction t a = a{transactions = t : transactions }
but it returns the following error:
automaton.hs:25:48: error:
• Couldn't match expected type ‘[Transaction]’
with actual type ‘Automaton -> [Transaction]’
• Probable cause: ‘transactions’ is applied to too few arguments
In the second argument of ‘(:)’, namely ‘transactions’
In the ‘transactions’ field of a record
In the expression: a {transactions = t : transactions}
|
25 | insert_transaction t a = a{transactions = t : transactions }
| ^^^^^^^^^^^^
How can I implement this? Thank you!
Upvotes: 0
Views: 52
Reputation: 120711
transactions
is not really a value of type [Transaction]
, it's by itself† only a record field accessor to a field of type [Transaction]
. I.e., it's a function. GHCi could have told you:
Main*> :t transactions
transactions :: Automaton -> Transactions
So, to access those transactions, you need to be explicit what automaton from, by just giving it as an argument:
insertTransaction :: Transaction -> Automaton -> Automaton
insertTransaction t a = a{ transactions = t : transactions a }
†“By itself” meaning: when you use it in a normal Haskell expression without special record syntax like a{transactions = ...}
. There, the transactions
isn't really an independent entity at all.
There are a couple of alternatives you could consider:
You can “partially pattern match” on the fields of a record you pass as a function argument.
insertTransaction t a{transactions = oldTransactions}
= a{ transactions = t : oldTransactions }
The RecordWildCards
extension can bring an entire record's fields in scope as overloaded variables, somewhat like as if you're in an OO method of the class containing those fields. This allows you to write essentially what you had originally:
{-# LANGUAGE RecordWildCards #-}
insertTransaction t a{..} = a{ transactions = t : transactions }
I would not recommend this: RecordWildCards
is an unidiomatic hack around the limitations of Haskell98' record types.
Lenses are the modern and consequent way to use records in Haskell. You can get them most easily by slightly changing your data definition:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Automaton = Automaton {
_initialState :: Int
, _states :: Set.Set Int
, _transactions :: [Transaction]
, _finalStates :: Set.Set Int
} deriving (Show)
makeLenses ''Automaton -- Generates `transactions` etc. not as plain accessor
-- functions, but as “optics” which can both get
-- and set field values.
insertTransaction :: Transaction -> Automaton -> Automaton
insertTransaction t = transactions %~ (t:)
Upvotes: 3