Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing algebraic data type from list

I'm a fairly new Haskell programmer and I'm trying to figure out how to get some values into an algebraic data type.

I have a record data type:

data OrbitElements = OrbitElements { epoch :: Double,
                                     ecc :: Double,
                                     distPeri :: Double,
                                     incl :: Double,
                                     longAscNode :: Double,
                                     argPeri :: Double,
                                     timePeri :: Double,
                                     meanMotion :: Double,
                                     meanAnomaly :: Double,
                                     trueAnomaly :: Double,
                                     semiMajorAxis :: Double,
                                     distApo :: Double,
                                     period :: Double
                                   }

I'm pulling in some info from a text file, which ends up in a list of Doubles. Is there an easy way to initialize this data type with the list? I could just call each setter individually but that seems terribly inefficient when I already have all the values in a list.

let d = [2456382.5,6.786842103348031e-3,0.7184187640759256,3.394660181513041,76.64395338801751,55.2296201483587,2456457.141012543,1.602144936476915,240.4142797010899,239.7408018186761,0.7233278761603762,0.7282369882448266,224.6987721295883]
let o = OrbitElements
let epoch o = d !! 0
let ecc o = d !! 1
-- and so on

What am I missing?

like image 934
James Curbo Avatar asked Mar 31 '13 21:03

James Curbo


Video Answer


1 Answers

The most direct way is to just do it by hand:

fromList :: [Double] -> Maybe OrbitElements
fromList [ _epoch
         , _ecc
         , _distPeri
         , _incl
         , _longAscNode
         , _argPeri
         , _timePeri
         , _meanMotion
         , _meanAnomaly
         , _trueAnomaly
         , _semiMajorAxis
         , _distApo
         , _period
         ]
    = Just $ OrbitElements
          _epoch
          _ecc
          _distPeri
          _incl
          _longAscNode
          _argPeri
          _timePeri
          _meanMotion
          _meanAnomaly
          _trueAnomaly
          _semiMajorAxis
          _distApo
          _period
fromList _ = Nothing

However, there is a slightly sexier way, which is to parse them element by element, which is less error-prone and more descriptive of what we are trying to do:

First we define two parsers, one of which requests a new element from the list (or fails if the list is empty), and the second of which matches the end of the list (or fails if the list is not empty):

import Control.Applicative
import Control.Monad
import Control.Monad.Trans.State

getElem :: StateT [Double] Maybe Double
getElem = do
    s <- get
    case s of
        []   -> mzero
        x:xs -> do
            put xs
            return x

endOfList :: StateT [Double] Maybe ()
endOfList = do
    s <- get
    case s of
        [] -> return ()
        _  -> mzero

Now we can define fromList in Applicative style:

fromList' :: [Double] -> Maybe OrbitElements
fromList' = evalStateT $ OrbitElements
    <$> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*> getElem
    <*  endOfList
like image 88
Gabriella Gonzalez Avatar answered Oct 08 '22 06:10

Gabriella Gonzalez