Haskell从do表示法传递到Applicative



我正试图从https://haskell-at-work.com/episodes/2018-01-19-domain-modelling-with-haskell-data-structures.html

但是我有一个错误,我不知道为什么。(可能只是意味着我不知道哈斯克尔(

Haskell代码:

项目.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)
数据库
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

错误

运行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)))
|     ^^^

这是一个简单的缩进错误。

请记住,let <definitions> in <expr>的语法允许存在多个定义的块"一块多个东西">1基本上总是Haskell中缩进重要的上下文;事物;块中必须从同一列开始,如果它们跨越多行,则连续行必须比块的对齐列缩进更多

这意味着这是好的:

something = let foo = 1
bar = 2
baz = 3
in foo + bar + baz

因为开始let块中的每个方程的fbb排在同一列。这也很好:

something = let
foo = 1
bar = 2
baz = 3
in foo + bar + baz

因为方程仍然是完全一致的。它们排列的位置实际上比let关键字本身缩进更少并不重要,in关键字甚至进一步缩进也不重要。

但这很糟糕:

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

因为let内部的定义块以rtn = []开始。第一个定义中的所有内容都必须比rtn中的r缩进得更远。要纠正它,你要么需要这样的东西:

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

下面的行缩进更多。或者(为了避免过度缩进(这样做:

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

有多种方法可以做到这一点;你也可以把getTransactions中的=放在下一行,或者把let放在前一行,只把rtn = []下移,等等。但一旦你开始定义块,你就必须继续规则,即块中条目的所有部分都必须进一步缩进;你不能重置回中间块。


1do块有多条语句,letwhere块有多个定义,case块有多个子分支,模块有多个导入和定义(通常我们在缩进级别0对齐这些,但您不必这样做(,等等。

其他任何东西的缩进都不重要,而且纯粹是惯例和可读性的问题(if/then/elselet/inin部分、函数定义的保护或其他部分等(

最新更新