[英]How to return a polymorphic type in Haskell based on the results of string parsing?
TL; DR:
如何在返回类型中编写多态的函数? 我正在进行一项练习,其任务是编写一个能够分析String
的函数,并根据其内容生成Vector [Int]
, Vector [Char]
或Vector [String]
。
更长的版本:
以下是一些预期函数的行为示例:
字符串"1 2\\n3 4"
将生成一个Vector [Int]
,它由两个列表组成: [1,2]
和[3,4]
。
字符串"'t' 'i' 'c'\\n't' 'a' 'c'\\n't' 'o' 'e'"
将生成一个Vector [Char]
(即由列表组成) "tic"
, "tac"
和"toe"
)。
字符串"\\"hello\\" \\"world\\"\\n\\"monad\\" \\"party\\""
将生成一个Vector [String]
(即["hello","world"]
和["monad","party"]
)。
错误检查/异常处理不是此特定练习的关注点。 在这个阶段,所有测试都是纯粹完成的,也就是说,这不是IO
monad的领域。
到目前为止我所拥有的:
我有一个函数(和新的数据类型),它能够对字符串进行分类。 我还有函数(每个Int
, Char
和String
),它可以将字符串转换为必要的Vector。
我尝试过的:
(如果我将三个转换函数填充到单个函数中(即,使用case..of
结构在字符串的case..of
进行模式匹配), VectorType
进行类型检查。
我尝试制作一个Vectorable
类,并为每种类型定义一个单独的实例; 我很快意识到这种方法只有在函数的参数因类型而异时才有效。 在我们的例子中,参数的类型不会改变(即,它总是一个String
)。
我的代码:
一些评论
解析: mySplitter
对象和mySplit
函数处理解析。 无可否认,这是一个基于Splitter
类型和Data.List.Split.Internals
的split
函数的粗略解析器。
分类: classify
函数能够根据字符串确定最终的VectorType
。
转换: toVectorNumber
, toVectorChar
和toVectorString
函数能够将字符串分别转换为Vector [Int]
, Vector [Char]
和Vector [String]
。
作为旁注,我正在根据导师的推荐尝试CorePrelude
。 这就是为什么你会看到我使用普通Prelude函数的通用版本。
码:
import qualified Prelude
import CorePrelude
import Data.Foldable (concat, elem, any)
import Control.Monad (mfilter)
import Text.Read (read)
import Data.Char (isAlpha, isSpace)
import Data.List.Split (split)
import Data.List.Split.Internals (Splitter(..), DelimPolicy(..), CondensePolicy(..), EndPolicy(..), Delimiter(..))
import Data.Vector ()
import qualified Data.Vector as V
data VectorType = Number | Character | TextString deriving (Show)
mySplitter :: [Char] -> Splitter Char
mySplitter elts = Splitter { delimiter = Delimiter [(`elem` elts)]
, delimPolicy = Drop
, condensePolicy = Condense
, initBlankPolicy = DropBlank
, finalBlankPolicy = DropBlank }
mySplit :: [Char]-> [Char]-> [[Char]]
mySplit delims = split (mySplitter delims)
classify :: String -> VectorType
classify xs
| '\"' `elem` cs = TextString
| hasAlpha cs = Character
| otherwise = Number
where
cs = concat $ split (mySplitter "\n") xs
hasAlpha = any isAlpha . mfilter (/=' ')
toRows :: [Char] -> [[Char]]
toRows = mySplit "\n"
toVectorChar :: [Char] -> Vector [Char]
toVectorChar = let toChar = concat . mySplit " \'"
in V.fromList . fmap (toChar) . toRows
toVectorNumber :: [Char] -> Vector [Int]
toVectorNumber = let toNumber = fmap (\x -> read x :: Int) . mySplit " "
in V.fromList . fmap toNumber . toRows
toVectorString :: [Char] -> Vector [[Char]]
toVectorString = let toString = mfilter (/= " ") . mySplit "\""
in V.fromList . fmap toString . toRows
Haskell不支持协变多态,如果是的话就不会有用。
基本上所有人都可以回答这个问题。 现在为什么会这样。
像OO语言一样“返回多态值”并不好,因为返回任何值的唯一原因是在其他函数中使用它 。 现在,在OO语言中,你没有函数, 只有对象附带的方法,所以“返回不同类型”很容易:每个都有内置的合适方法,并且每个实例都可以变化。 (这是一个好主意是另一个问题。)
但在Haskell中,功能来自其他地方。 他们不知道特定实例的实现更改,因此可以安全地定义这些函数的唯一方法是了解每个可能的实现 。 但是如果你的返回类型真的是多态的,那是不可能的,因为多态是一个“开放”的概念(它允许随后添加新的实现变种)。
相反,Haskell有一个非常方便且完全安全的机制来描述一组封闭的“实例” - 你实际上已经自己使用它了! 抽象数据类型。
data PolyVector = NumbersVector (Vector [Int])
| CharsVector (Vector [Char])
| StringsVector (Vector [String])
这是你想要的返回类型。 这个函数不会是多态的,它只会返回一个更通用的类型。
现在...... 实际上 ,Haskell确实有办法对“多态返回”进行排序。 在OO中,当您声明返回指定类的子类时。 好吧,你不能在Haskell中“返回一个类”,你只能返回类型。 但是那些可以用来表达“......的任何实例”。 它被称为存在量化 。
{-# LANGUAGE GADTs #-}
data PolyVector' where
PolyVector :: YourVElemClass e => Vector [e] -> PolyVector'
class YourVElemClass where
...?
instance YourVElemClass Int
instance YourVElemClass Char
instance YourVElemClass String
我不知道这对你来说是否有点吸引力。 事实是,它更复杂,使用起来更加困难; 你不仅可以直接获得任何可能的结果,而且只能通过YourVElemClass
方法来使用这些元素。 GADT在某些应用中可能非常有用,但这些通常涉及具有非常深刻的数学动机的类。 YourVElemClass
似乎没有这样的动机,因此,与存在量化相比,使用简单的ADT替代方案会更好。
Luke Palmer有一个着名的反对存在的咆哮 (注意他使用另一种语法,存在性特定的,我认为它是过时的,因为GADT更严格一般)。
简单,使用总和类型!
data ParsedVector = NumberVector (Vector [Int]) | CharacterVector (Vector [Char]) | TextString (Vector [String]) deriving (Show)
parse :: [Char] -> ParsedVector
parse cs = case classify cs of
Number -> NumberVector $ toVectorNumber cs
Character -> CharacterVector $ toVectorChar cs
TextString -> TextStringVector $ toVectorString cs
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.