编写适当的自定义读取实例
Writing a proper custom read instance
大家好Haskell各位,
我正在学习 Haskell 从一个月开始,我正在努力为个人数据类型创建自定义读取实例。
我关注了 this and the relevant chapter in Learn Yourself a Haskell,这是我的代码片段。
data Position = Position (Absc,Ordn) deriving (Show)
instance Read (Position) where
readsPrec _ input =
let absc = List.filter (/='(') $ takeWhile (/=',')
ordn = List.filter (/=')') $ tail (dropWhile (/=',') )
in (\str -> Position ( (read (absc str) :: Int)
, (read (ordn str) :: Int) ) ) input
type Absc = Int
type Ordn = Int
我的目标是解析输入 "(1,3)"
以输出类似 Position (1,3)
的内容
但是,我收到以下错误消息:
• Couldn't match expected type ‘[Char]’
with actual type ‘[Char] -> [Char]’
• Probable cause: ‘takeWhile’ is applied to too few arguments
In the second argument of ‘($)’, namely ‘takeWhile (/= ',')’
In the expression: filter (/= '(') $ takeWhile (/= ',')
In an equation for ‘absc’:
absc = filter (/= '(') $ takeWhile (/= ',')
ordn 函数也一样。
• Couldn't match expected type ‘[(Position, String)]’
with actual type ‘Position’
• In the expression:
(\ str
-> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
input
In the expression:
let
absc = filter (/= '(') $ takeWhile (/= ',')
ordn = filter (/= ')') $ tail (dropWhile (/= ','))
in
(\ str
-> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
input
In an equation for ‘readsPrec’:
readsPrec _ input
= let
absc = filter (/= '(') $ takeWhile (/= ',')
ordn = filter (/= ')') $ tail (dropWhile (/= ','))
in
(\ str
-> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
input
我的 let
语句似乎无法将 absc
和 ordn
识别为函数(或者至少尝试直接应用它们,而我只想将它们定义为部分应用函数稍后在参数 str
处应用它们)。我也可能搞砸了我的 Position
值构造函数。
我不熟悉 Haskell 编码风格,我可能使用了一些我不完全理解的关键字和工具。你能提示我如何编写它以使其工作吗?
提前致谢。
您的代码中有很多错误 - 我认为您应该仔细阅读一下 $
和 .
之间的区别,这是处理函数时必须知道的,大多数情况下您的错误是由于尝试将函数应用于其参数 ($)
而不是连接函数 (.)
.
这就是我修改您的代码以至少进行类型检查的内容 - 虽然我认为 readsPrec
不应该保留原始输入,而是解析完成后的剩余字符串。
修复代码
instance Read Position where
readsPrec _ input =
let absc = filter (/='(') . takeWhile (/=',')
ordn = filter (/=')') . tail . dropWhile (/=',')
in [(Position ( read (absc input) :: Int
, read (ordn input) :: Int), input)]
但是看到这个让我很不高兴- haskell 不是说抽象,圆滑和真正的表现力。让我们再试一次
整理中
instance Read Position where
readsPrec _ str = let ('(':absc,',':ordn') = break (==',') str
(ordn,')':str') = break (==')') ordn'
in [(Position (read absc, read ordn), str')]
好多了,我们捕捉到开头和结尾应该有括号,但还是有点麻烦。
使用现有功能
知道 Tuple
已经是 Read
的实例
instance Read Position where
readsPrec s str = [(Position x, rest) | (x,rest) <- readsPrec s str]
不错,但我们还可以做得更好
假设我们最近对元组进行了一些研究,发现非常方便的模块 Data.Bifunctor
具有函数 first
和 second
来转换二元组的第一个和第二个组件(实际上是任何双函子)。
我们可以将上面的简化为
instance Read Position where
readsPrec s = map (first Position) . readsPrec s
干净而短小。
使用 show "(3,4)"::Position
returns Position (3,4)
。
非常感谢您的详细回答。我倾向于混淆 $
和 .
符号,但在阅读文档后,它变得清晰了。
大家好Haskell各位,
我正在学习 Haskell 从一个月开始,我正在努力为个人数据类型创建自定义读取实例。
我关注了 this and the relevant chapter in Learn Yourself a Haskell,这是我的代码片段。
data Position = Position (Absc,Ordn) deriving (Show)
instance Read (Position) where
readsPrec _ input =
let absc = List.filter (/='(') $ takeWhile (/=',')
ordn = List.filter (/=')') $ tail (dropWhile (/=',') )
in (\str -> Position ( (read (absc str) :: Int)
, (read (ordn str) :: Int) ) ) input
type Absc = Int
type Ordn = Int
我的目标是解析输入 "(1,3)"
以输出类似 Position (1,3)
但是,我收到以下错误消息:
• Couldn't match expected type ‘[Char]’
with actual type ‘[Char] -> [Char]’
• Probable cause: ‘takeWhile’ is applied to too few arguments
In the second argument of ‘($)’, namely ‘takeWhile (/= ',')’
In the expression: filter (/= '(') $ takeWhile (/= ',')
In an equation for ‘absc’:
absc = filter (/= '(') $ takeWhile (/= ',')
ordn 函数也一样。
• Couldn't match expected type ‘[(Position, String)]’
with actual type ‘Position’
• In the expression:
(\ str
-> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
input
In the expression:
let
absc = filter (/= '(') $ takeWhile (/= ',')
ordn = filter (/= ')') $ tail (dropWhile (/= ','))
in
(\ str
-> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
input
In an equation for ‘readsPrec’:
readsPrec _ input
= let
absc = filter (/= '(') $ takeWhile (/= ',')
ordn = filter (/= ')') $ tail (dropWhile (/= ','))
in
(\ str
-> Position ((read (absc str) :: Int), (read (ordn str) :: Int)))
input
我的 let
语句似乎无法将 absc
和 ordn
识别为函数(或者至少尝试直接应用它们,而我只想将它们定义为部分应用函数稍后在参数 str
处应用它们)。我也可能搞砸了我的 Position
值构造函数。
我不熟悉 Haskell 编码风格,我可能使用了一些我不完全理解的关键字和工具。你能提示我如何编写它以使其工作吗?
提前致谢。
您的代码中有很多错误 - 我认为您应该仔细阅读一下 $
和 .
之间的区别,这是处理函数时必须知道的,大多数情况下您的错误是由于尝试将函数应用于其参数 ($)
而不是连接函数 (.)
.
这就是我修改您的代码以至少进行类型检查的内容 - 虽然我认为 readsPrec
不应该保留原始输入,而是解析完成后的剩余字符串。
修复代码
instance Read Position where
readsPrec _ input =
let absc = filter (/='(') . takeWhile (/=',')
ordn = filter (/=')') . tail . dropWhile (/=',')
in [(Position ( read (absc input) :: Int
, read (ordn input) :: Int), input)]
但是看到这个让我很不高兴- haskell 不是说抽象,圆滑和真正的表现力。让我们再试一次
整理中
instance Read Position where
readsPrec _ str = let ('(':absc,',':ordn') = break (==',') str
(ordn,')':str') = break (==')') ordn'
in [(Position (read absc, read ordn), str')]
好多了,我们捕捉到开头和结尾应该有括号,但还是有点麻烦。
使用现有功能
知道 Tuple
已经是 Read
instance Read Position where
readsPrec s str = [(Position x, rest) | (x,rest) <- readsPrec s str]
不错,但我们还可以做得更好
假设我们最近对元组进行了一些研究,发现非常方便的模块 Data.Bifunctor
具有函数 first
和 second
来转换二元组的第一个和第二个组件(实际上是任何双函子)。
我们可以将上面的简化为
instance Read Position where
readsPrec s = map (first Position) . readsPrec s
干净而短小。
使用 show "(3,4)"::Position
returns Position (3,4)
。
非常感谢您的详细回答。我倾向于混淆 $
和 .
符号,但在阅读文档后,它变得清晰了。