Home > Software design >  Haskell Passing from do notation to Applicative (Part 2)
Haskell Passing from do notation to Applicative (Part 2)

Time:04-12

I am trying to strip away do notation in the Database.sh file from https://haskell-at-work.com/episodes/2018-01-19-domain-modelling-with-haskell-data-structures.html

But I am having an Error and I have no Idea why. (Probably just means I don't know about Haskell)

This is a continuation of Haskell Passing from do notation to Applicative

Haskell Code:

Project.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Project where

import           Data.Text (Text)

newtype Money = Money
  { unMoney :: Double
  } deriving (Show, Eq, Num)

newtype ProjectId = ProjectId
  { unProjectId :: Int
  } deriving (Show, Eq, Num)

data Project
  = Project ProjectId
            Text
  | ProjectGroup Text
                 [Project]
  deriving (Show, Eq)

data Budget = Budget
  { budgetIncome      :: Money
  , budgetExpenditure :: Money
  } deriving (Show, Eq)

data Transaction
  = Sale Money
  | Purchase Money
  deriving (Eq, Show)

Database
import           System.Random (getStdRandom, randomR)

import Project

getBudget :: ProjectId -> IO Budget
getBudget _ = Budget
    <$> (Money <$> getStdRandom (randomR (0, 10000)))
    <*> (Money <$> getStdRandom (randomR (0, 10000)))

getTransactions :: ProjectId -> IO [Transaction]
getTransactions _ = 
  let rtn = []
       <$>  (Sale . Money <$> getStdRandom (randomR (0, 4000)))
       <*> (Purchase . Money <$> getStdRandom (randomR (0, 4000)))
  in
    pure rtn

Error

After running stack ghc Database.hs --package random


Database.hs:12:13: error:
    * Couldn't match expected type: Transaction -> Transaction -> b
                  with actual type: [a0]
    * In the first argument of `(<$>)', namely `[]'
      In the first argument of `(<*>)', namely
        `[] <$> (Sale . Money <$> getStdRandom (randomR (0, 4000)))'
      In the expression:
        [] <$> (Sale . Money <$> getStdRandom (randomR (0, 4000)))
          <*> (Purchase . Money <$> getStdRandom (randomR (0, 4000)))
    * Relevant bindings include rtn :: f b (bound at Database.hs:12:7)
   |
12 |   let rtn = []
   |             ^^

CodePudding user response:

In

let rtn = []
     <$>  (Sale . Money <$> getStdRandom (randomR (0, 4000)))
     <*> (Purchase . Money <$> getStdRandom (randomR (0, 4000)))

It looks like you are trying to make a list of the results of those two actions? That doesn't work because the first argument of (<$>) has to be a function, and [] is not a function (among other reasons). If you want to keep this notation, you should replace with a pairing function:

pair x y = [x, y]

then

let rtn = pair <$> ...

But really I think the appropriate combinator here is sequenceA

sequenceA :: (Applicative f) => [f a] -> f [a]

which takes a list of actions, and gives back an action returning a list of results.

So:

let rtn = sequenceA [Sale . Money <$> getStdRandom (randomR (0, 4000)),
                     Purchase . Money <$> getStdRandom (randomR (0, 4000))]

Oh, and there should not be a pure to return this. It's already of the correct type, it does not need to be lifted into IO.

Incidentally, getStdRandom . randomR is also known as randomRIO.

  • Related