我只能设法获得这个可怕的解决方案。
{-# 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,但目前我不知道该怎么做。
可取之处在于该方法非常通用,可以很容易地适应其他类型的构造函数。