Haskell 从 do 符号传递到 Applicative(第 2 部分)

Haskell Passing from do notation to Applicative (Part 2)

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

中删除 Database.sh 文件中的 do 符号

但是我有一个错误,我不知道为什么。 (可能只是表示我不知道 Haskell)

这是

的延续

Haskell代码:

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)

数据库
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


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 = []
   |             ^^

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

您似乎在尝试列出这两个操作的结果?这是行不通的,因为 (<$>) 的第一个参数必须是函数,而 [] 不是函数(还有其他原因)。如果你想保留这个符号,你应该用配对函数替换:

pair x y = [x, y]

然后

let rtn = pair <$> ...

但我真的认为这里合适的组合器是 sequenceA

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

它接受一个动作列表,并返回一个动作return一个结果列表。

所以:

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

哦,不应该有pure到return这个。已经是正确的类型了,不需要提升成IO.

顺便说一句,getStdRandom . randomR 也称为 randomRIO