【问题标题】:Printing Dynamic Data打印动态数据
【发布时间】:2018-08-03 17:45:07
【问题描述】:

我在 haskell 中有一个系统,它使用 Data.Dynamic 和 Type.Reflection 来执行推理和计算。我希望能够打印结果。

提供类型时打印很容易,例如

foo :: Dynamic -> String
foo dyn = case tyConName . someTypeRepTyCon . dynTypeRep $ dyn of
             "Int"  -> show $ fromDyn dyn (0 :: Int)
             "Bool" -> show $ fromDyn dyn True
             _      -> "no chance"

但如果我希望能够打印元组,则必须为每个元组添加一个新行,例如 (Int, Bool), (Bool, Int), (Char, Int, Banana) ....

随着更多基元和更大元组的添加,这很快变得不切实际。

是否有一种算法方法可以为此动态数据生成字符串,特别是元组和列表。

【问题讨论】:

    标签: haskell generics types


    【解决方案1】:

    我只能设法获得这个可怕的解决方案。

    {-# LANGUAGE GADTs, ScopedTypeVariables, TypeApplications #-}
    {-# OPTIONS -Wall #-}
    
    import Type.Reflection
    import Data.Dynamic
    

    这里我们为(,) 和Int 定义TyCon。 (我很确定一定有更简单的方法。)

    pairTyCon :: TyCon
    pairTyCon = someTypeRepTyCon (someTypeRep [('a','b')])
    
    intTyCon :: TyCon
    intTyCon = someTypeRepTyCon (someTypeRep [42 :: Int])
    

    然后我们剖析Dynamic 类型。首先我们检查它是否是Int。

    showDynamic :: Dynamic -> String
    showDynamic x = case x of
       Dynamic tr@(Con k) v | k == intTyCon ->
          case eqTypeRep tr (typeRep @ Int) of
            Just HRefl -> show (v :: Int)
            _ -> error "It really should be an int"
       -- to be continued
    

    上面的内容很难看,因为我们首先使用==而不是模式匹配来对TyCon进行模式匹配,这阻止了v的类型细化为Int。所以,我们仍然需要求助于eqTypeRep 来执行我们已经知道必须成功的第二次检查。

    例如,我认为可以通过提前检查eqTypeRep 来使其更漂亮。或fromDyn。没关系。

    重要的是,下面这对案例更加凌乱,据我所知,不能以同样的方式变得漂亮。

       -- continuing from above
       Dynamic tr@(App (App t0@(Con k :: TypeRep p)
                (t1 :: TypeRep a1))
                (t2 :: TypeRep a2)) v | k == pairTyCon ->
          withTypeable t0 $
          withTypeable t1 $
          withTypeable t2 $
          case ( eqTypeRep tr (typeRep @(p a1 a2))
               , eqTypeRep (typeRep @p) (typeRep @(,))) of
            (Just HRefl, Just HRefl) ->
                "DynamicPair("
                ++ showDynamic (Dynamic t1 (fst v))
                ++ ", "
                ++ showDynamic (Dynamic t2 (snd v))
                ++ ")"
            _ -> error "It really should be a pair!"
       _ -> "Dynamic: not an int, not a pair"
    

    上面我们匹配TypeRep,所以它代表p a1 a2类型的东西。我们要求p 的表示为pairTyCon。

    和之前一样,这不会触发类型优化,因为它是使用== 而不是模式匹配完成的。我们需要执行另一个显式匹配来强制p ~ (,) 和另一个用于最终细化v :: (a1,a2)。叹息。

    最后,我们可以将fst v 和snd v 再次变成Dynamic,然后将它们配对。实际上,我们将原来的x :: Dynamic 变成了类似(fst x, snd x) 的东西,其中两个组件都是Dynamic。现在我们可以递归了。

    我真的很想避免errors,但目前我不知道该怎么做。

    可取之处在于该方法非常通用,可以很容易地适应其他类型的构造函数。

    【讨论】:

    • 有一个函数可以获取构造函数的字符串代表,tyConName 我一直在使用它来测试构造函数类型。 (仍在阅读您的答案)
    • 效果很好,谢谢。我认为我遇到的主要问题是忘记使用 withTypeable。
    【解决方案2】:

    我喜欢另一个答案的主要思想,但它似乎以一种相当迂回的方式到达了它的去向。以下是我将如何设计相同的想法:

    {-# LANGUAGE ViewPatterns #-}
    {-# LANGUAGE TypeApplications #-}
    {-# LANGUAGE GADTs #-}
    import Type.Reflection
    import Data.Dynamic
    
    showDyn :: Dynamic -> String
    showDyn (Dynamic (App (App (eqTypeRep (typeRep @(,)) -> Just HRefl) ta) tb) (va, vb))
        = concat [ "DynamicPair("
                 , showDyn (Dynamic ta va)
                 , ","
                 , showDyn (Dynamic tb vb)
                 , ")"
                 ]
    showDyn (Dynamic (eqTypeRep (typeRep @Integer) -> Just HRefl) n) = show n
    showDyn (Dynamic tr _) = show tr
    

    第一个模式匹配是相当拗口的,但是在尝试了几种不同的格式化方式之后,我确信没有办法让它看起来很好。你可以在 ghci 中试试:

    > showDyn (toDyn ((3,4), (True, "hi")))
    "DynamicPair(DynamicPair(3,4),DynamicPair(Bool,[Char]))"
    

    【讨论】:

      猜你喜欢
      • 2019-05-31
      • 1970-01-01
      • 2017-01-18
      • 2015-08-14
      • 1970-01-01
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 2012-05-07
      相关资源
      最近更新 更多