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)
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
[2 of 2] Compiling Database ( Database.hs, Database.o )
Database.hs:12:5: error: parse error on input `<$>'
|
12 | <$> (Sale . Money <$> getStdRandom (randomR (0, 4000)))
| ^^^
CodePudding user response:
This is a simple indentation error.
Remember that the syntax of let <definitions> in <expr>
allows there to be a block of multiple definitions. "A block of multiple things"1 is basically always the context where indentation matters in Haskell, and the rule is always that each of the "things" in the block must start at the same column and if they span more than one line the continuation lines must all be more indented than the alignment column for the block.
That means that this is good:
something = let foo = 1
bar = 2
baz = 3
in foo bar baz
Because the f
, b
, and b
starting each of the equations in the let
block line up at the same column. This is also good:
something = let
foo = 1
bar = 2
baz = 3
in foo bar baz
Because the equations still line up exactly. It does not matter that the position they line up at is actually less indented than the let
keyword itself, nor does it matter that the in
keyword is even further indented.
But this is bad:
getTransactions :: ProjectId -> IO [Transaction]
getTransactions _ = let rtn = []
<$> (Sale . Money <$> getStdRandom (randomR (0, 4000)))
<*> (Purchase . Money <$> getStdRandom (randomR (0, 4000)))
in
pure rtn
Because the block of definitions inside the let
starts with rtn = []
. Everything that's part of that first definition has to be indented further than the r
from rtn
. To correct it you either need something like:
getTransactions :: ProjectId -> IO [Transaction]
getTransactions _ = let rtn = []
<$> (Sale . Money <$> getStdRandom (randomR (0, 4000)))
<*> (Purchase . Money <$> getStdRandom (randomR (0, 4000)))
in
pure rtn
Where you indent the following lines more. Or (to avoid excessive indentation) do something like this:
getTransactions :: ProjectId -> IO [Transaction]
getTransactions _ =
let rtn = []
<$> (Sale . Money <$> getStdRandom (randomR (0, 4000)))
<*> (Purchase . Money <$> getStdRandom (randomR (0, 4000)))
in
pure rtn
There are various ways you can do this; you could put the =
from getTransactions
on the next line too, or you could leave the let
on the previous line and only move the rtn = []
down, etc. But once you start the block of definitions, you have to continue the rule that everything that's part of an entry in the block must be further indented; you can't reset back out mid-block.
1 do
blocks have multiple statements, let
and where
blocks have multiple definitions, case
blocks have multiple branches, modules have multiple imports and definitions (conventionally we align these at indentation level 0, but you don't have to), etc, etc.
The indentation of anything else does not matter, and is purely a matter of convention and readibility (if/then/else
, the in
part of a let/in
, guards or other parts of a function definition, etc)