简体   繁体   中英

Haskells data types and constructors with functions?

I'm new to Haskell and I'm looking at basic data types and constructors with functions.

I've done the below code:

data Name = Name String deriving (Show)
data Age = Age Int deriving (Show)
data Iq = Iq Int deriving (Show)
data Language = Language String deriving (Show)
data DataSubject = DSInformation Name Age Iq Language | DSConstruct {name :: String, age :: Int, iq :: Int, language :: String} deriving (Show)

makeDataSubject :: DataSubject -> DataSubject --Take in info and output a record
makeDataSubject (DSInformation (Name n) (Age a) (Iq i) (Language l)) = (DSConstruct {name = n, age = a, iq = i, language = l})

main = do 
  let x = makeDataSubject $ (DSInformation (Name "Ron") (Age 34) (Iq 100) (Language "French"))
  putStrLn $ show x

Runs fine, however it seems overly verbose -- how can I make to make it better?

Most of your data declarations can probably be simple type aliases.

type Name = String
type Age = Int
type Iq = Int
type Language = String

With these aliases, there is no significant difference (record syntax aside) between the two constructors for DataSubject . Get rid of one, and dispense with makeDataSubject . (Unless you want to encapsulate some logic or prevent pattern matching, you don't need a smart constructor to do what you are doing.)

data DataSubject = DS { name :: Name
                      , age :: Age
                      , iq :: Iq
                      , language :: Language
                   } deriving (Show)

main = do 
        let x = DS { name="Ron", age=34, iq=100, language="French"}
        putStrLn $ show x

If you do want real types, not just aliases, use newtype instead of data .

newtype Name = Name String deriving Show
newtype Age = Age Int deriving Show
newtype Iq = Iq Int deriving Show
newtype Language = Language String deriving Show

data DataSubject = DS { name :: Name
                      , age :: Age
                      , iq :: Iq
                      , language :: Language
                      } deriving (Show)

main = do
         let x = DS { name=Name "Ron", age=Age 34, iq=Iq 100, language=Language "French"}
         putStrLn $ show x

You might want to add a smart constructor here, but have it take each piece of data as a separate argument (wrapped or unwrapped), not a single argument that is already the return value up to isomorphism. (That is, your constructor was essentially the identity function other than some repackaging of the input.)

makeDataSubject :: String -> Int -> Int -> String -> DataSubject
makeDataSubject name age iq lang = DS {name=Name name, age=Age age, iq=Iq iq, language=Language lang}

or

makeDataSubject' :: Name -> Age -> Iq -> Language -> DataSubject
makeDataSubject' name age iq lang = DS {name=name, age=age, iq=iq, language=lang}

Unfortunately you are running into one of Haskell's weaknesses: the record system. It would be nice if we had some sort notation to express subject.name and subject.age rather than destructuring explicitly but right now there is no good answer. Work coming down the pipeline in GHC 8 should address the problem soon, however, and there are are all sorts of libraries working in the problem space. But, for this question specifically, we can employ a simple trick: -XRecordWildcards .

{-# LANGUAGE RecordWildCards #-}

module Main where

newtype Name = Name String deriving Show
newtype Age = Age Int deriving Show
newtype Iq = Iq Int deriving Show
newtype Language = Language String deriving Show

data DataSubject =
    DSInformation Name Age Iq Language
  | DSConstruct {name :: String, age :: Int, iq :: Int, language :: String}
  deriving Show

-- | Take in info and output a record
makeDataSubject :: DataSubject -> DataSubject
makeDataSubject (DSInformation (Name name) (Age age) (Iq iq) (Language language)) =
  DSConstruct {..}

main :: IO ()
main =
  print . makeDataSubject $ DSInformation (Name "Ron") (Age 34) (Iq 100) (Language "French")

By destructuring into the names of the fields, {..} will pick up on those bindings in scope to automatically populate the fields. You will absolutely want to turn on -Wall -Werror during compilation because it will be now more than ever easier to misspell something and forget to populate a field and then you end up with a partial record (another wart of the records system) where some fields are left undefined .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM