很遗憾,read
遇到这样的情况,已经束手无策了。让我们回顾一下read
:
read :: Read a => String -> a
如你看到的,a
不依赖于输入,而仅依赖于输出,因此也依赖于我们函数的上下文。如果你使用read a + read b
,那么额外的Num
上下文会将类型限制为Integer
or Double
due to default
规则。让我们看看它的实际效果:
> :set +t
> read "1234"
*** Exception: Prelude.read: no parse
> read "1234" + read "1234"
2468
it :: (Num a, Read a) => a
Ok, a
仍然没有帮助。是否有任何类型我们可以在没有额外上下文的情况下阅读?当然,单位:
> read "()"
()
it :: Read a => a
这仍然没有任何帮助,所以让我们启用单态限制 https://stackoverflow.com/q/32496864/1139697:
> :set -XMonomorphismRestriction
> read "1234" + read "1234"
2468
it :: Integer
啊哈。最后,我们有一个Integer
。由于+
,我们必须决定一种类型。现在,随着MonomorphismRestriction
启用后会发生什么read "1234"
没有额外的上下文?
> read "1234"
<interactive>:20:1
No instance for (Read a0) arising from a use of 'read'
The type variable 'a0' is ambiguous
现在 GHCi 不选择任何(默认)类型和力量you选择一个。这使得潜在的错误更加清晰。
那么我们该如何解决这个问题呢?由于 CSV 可以在运行时包含任意字段,并且所有类型都是静态确定的,因此我们必须通过引入类似的内容来作弊
data CSVField = CSVString String | CSVNumber Double | CSVUnknown
然后写
parse :: Field -> CSVField
毕竟我们的类型需要覆盖all可能的字段。
但是,对于您的情况,我们可以限制read's
type:
myRead :: String -> Double
myRead = read
但这并不明智,因为如果该列不包含,我们仍然可能会遇到错误Double
首先。所以,让我们使用readMaybe
and mapM
:
columnAsNumbers :: [Field] -> Maybe [Double]
columnAsNumbers = mapM readMaybe
这样,类型就固定了,我们被迫检查是否有Just
某事或Nothing
:
mean <$> columnAsNumbers (indexFields records 2)
如果您发现自己经常使用columnAsNumbers
不过,创建一个运算符:
(!!$) :: [[Field]] -> Maybe [Double]
records !!$ index = columnAsNumbers $ indexFields records index