【问题标题】:Usecase of Variants in Purescript/HaskellPurescript/Haskell 中变体的用例
【发布时间】:2022-12-02 21:38:32
【问题描述】:

有人能告诉我purescript-variants 或一般变体的用例是什么吗

文档写得很好,但我找不到任何真实的用例场景。有人能告诉我们如何在现实世界中使用 Variants 吗?

【问题讨论】:

    标签: functional-programming variant purescript


    【解决方案1】:

    变体是记录的对偶。虽然记录是一种可扩展的临时产品类型(考虑 data T = T Int Stringtype T = { i :: Int, s :: String }),但变体可以看作是可扩展的临时总和类型 - 例如data T = A Int | B StringVariant (a :: Int, b :: String)

    例如,就像您可以编写处理部分记录的函数一样:

    fullName :: forall r. { first :: String, last :: String | r } -> String
    fullName r = r.first <> " " <> r.last
    
    myFullName = fullName { first: "Fyodor", last: "Soikin", weight: "Too much" }
    

    同样,您可以编写一个处理部分变体的函数:

    weight :: forall r. Variant (kilos :: Int, vague :: String | r) -> String
    weight = 
      default "Unknown"
      # on _kilos (
     -> show n <> " kg.")
      # on _vague (s -> "Kind of a " <> s)
    
    myWeight = weight (inj _kilos 100)  -- "100 kg."
    
    alsoMyWeight = weight (inj _vague "buttload")  -- "Kind of a buttload"
    

    但这些当然是玩具示例。对于一个不那么玩具的例子,我会想象一些处理替代品的东西,但需要是可扩展的。也许类似于文件解析器:

    data FileType a = Json | Xml
    
    basicParser :: forall a. FileType a -> String -> Maybe a
    basicParser t contents = case t of
      Json -> parseJson contents
      Xml -> parseXml contents
    

    假设在大多数情况下我可以使用这个解析器,但在某些情况下我也希望能够解析 YAML。我该怎么办?我不能“扩展”FileType sum 类型事后,我能做的最好的就是将它聚合成一个更大的类型:

    data BetterFileType a = BasicType (FileType a) | Yaml
    
    betterParser :: forall a. BetterFileType a -> String -> Maybe a
    betterParser t contents = case t of
      BasicType bt -> basicParser bt contents
      Yaml -> parseYaml contents
    

    现在每当我调用“更好的解析器”时,我都必须笨拙地包装文件类型:

    result = betterParser (BasicType Json) "[1,2,3]"
    

    更糟的是:现在每个消费者都必须知道BetterFileType -> FileType 的层次结构,他们不能只说“json”,他们必须知道将其包装在BasicType 中。尴尬的。

    但是如果我为文件类型使用可扩展的变体,我可以很好地展平它们:

    type FileType r = (json :: String, xml :: String | r)
    
    basicParser :: forall a r. Variant (FileType r) -> Maybe a
    basicParser = onMatch { json: parseJson, xml: parseXml } $ default Nothing
    ----
    type BetterFileType r = (yaml :: String | FileType r)
    
    betterParser :: forall a r. Variant (BetterFileType r) -> Maybe a
    betterParser = onMatch { yaml: parseYaml } basicParser
    

    现在我可以将裸变体名称与 basicParserbetterParser 一起使用,而不知道是否包装它们:

    r1 = betterParser $ inj _json "[1,2,3]"
    r2 = betterParser $ inj _yaml "foo: [1,2,3]"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-31
      • 2020-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多