本文介绍了GHC泛型:如何编写将和类型从/转换为整数的(:+ :)的实现?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 限时删除!! 我想写一个实现 实例(GMySerialize a,GMySerialize b)=> GMySerialize(a:+:b) 其中GMySerialize定义为: class GMySerialize f where gtoMyS :: fa - > MySerialize gfromMyS :: MySerialize - >可能(fa) 对于任何只包含空数据构造函数的和类型(例如 data MyType = A | B | C | D | E | f ),将它转换为 MySerializeInt code> MySerializeInt 是 MySerialize 的构造函数,它接受一个int参数。 instance(GMySerialize a,GMySerialize b)=> GMySerialize(a:+:b)其中 gtoMyS(L1 x)= MySerializeInt(0 + rest) where rest = case gtoMyS x of MySerializeInt n - > n MySerializeNil - > 0 err - >错误$ show err gtoMyS(R1 x)= MySerializeInt(1+ rest) where rest = case gtoMyS x of MySerializeInt n - > n MySerializeNil - > 0 err - >错误$ show err 但是意识到这是非常错误的,我不确定如何解决它。它怎么错了?作为一个例子,下面的代码产生相同的整数,但它们不应该像它们代表不同的构造函数一样: M1 {unM1 = L1 (R1(M1 {unM1 = U1}))} M1 {unM1 = R1(L1(M1 {unM1 = U1}))} 我也不确定如何编写 gfromMyS 实例,即使我得到了为了用另一种方式来说话,我期望做的事情与编写一个模板Haskell函数具有同等效果,该函数生成了一个模板对象: pre $ c $ instance MySerialize t其中 toMyS x = MySerializeInt(toEnum x) fromMyS(MySerializeInt n) - > Just(fromEnum n) fromMyS _ - > Nothing 对于每一个 t 其中 t 是仅带有实现 Enum 的无穷构造函数的求和类型。 解决方案诀窍是创建另一个类来计算构造函数的数量 { - #LANGUAGE ScopedTypeVariables# - } { - #LANGUAGE TypeOperators# - } { - #LANGUAGE DefaultSignatures# - } { #语言DeriveGeneric# - } { - #LANGUAGE KindSignatures# - } { - #LANGUAGE FlexibleInstances# - } { - #LANGUAGE FlexibleContexts# - } import Data.Functor((< $>)) import Data.Tagged import GHC.Generics class GNumConstructors(f :: * - > ; *)其中 - 这与CAF足够接近以便在字典中记忆吗? gnumConstructors :: Tagged f Int 实例GNumConstructors(M1 C cf)其中 gnumConstructors =标记1 实例(GNumConstructors a,GNumConstructors b) => GNumConstructors(a:+:b)其中 gnumConstructors =标记$ unTagged(gnumConstructors :: Tagged a Int)+未标记(gnumConstructors :: Tagged b Int) 然后你可以很容易地将左边的数字(小于左边的数字)和右边的数字(任何更大的数字)。 type MyS = Int class GMySerialize f其中 gtoMyS :: fa - > MyS gfromMyS :: MyS - >也许(f a) 实例(GNumConstructors a,GMySerialize a,GMySerialize b)=> GMySerialize(a:+:b)其中 gtoMyS(L1 l)= gtoMyS l gtoMyS(R1 r)= unTagged(gnumConstructors :: Tagged a Int)+ gtoMyS r gfromMyS x =如果x 然后L1< $> gfromMyS x else R1< $> gfromMyS(x - unagagged(gnumConstructors :: Tagged a Int)) 任何单独的构造函数都由0我们直接浏览元数据。 instance GMySerialize U1其中 gtoMyS U1 = 0 gfromMyS 0 =只是U1 gfromMyS _ = Nothing 实例GMySerialize f => GMySerialize(M1 i c f)其中 gtoMyS(M1 a)= gtoMyS a gfromMyS ms = M1 $> gfromMyS ms 结合 MySerialize 可以为 MyType 填充一个完整的示例并对其进行测试 class MySerialize a where toMyS :: a - > MyS fromMyS :: MyS - >也许一个 默认为toMyS ::(Generic a,GMySerialize(Rep a))=> a - > MyS toMyS a = gtoMyS $ from a default fromMyS ::(Generic a,GMySerialize(Rep a))=> MyS - >也许一个 fromMyS a = to< $> gfromMyS a data MyType = A | B | C | D | E | F 导出(Generic,Show) 实例MySerialize MyType main =做打印。映射到MYS $ [A,B,C,D,E,F] 打印。 map(fromMyS :: MyS - > Maybe MyType)$ [-1,0,1,2,3,4,5,6] A 到 F 被映射到数字 0 到 5 。阅读这些数字会再现 A 到 F 。尝试读取该范围之外的数字会产生 Nothing 。 [ 0,1,2,3,4,5] [Nothing,Just A,Just B,Just C,Just D,Just E,Just F,Nothing] I would like to write an implementation ofinstance (GMySerialize a, GMySerialize b) => GMySerialize (a :+: b)Where GMySerialize is defined as:class GMySerialize f where gtoMyS :: f a -> MySerialize gfromMyS :: MySerialize -> Maybe (f a)That will, for any sum type consisting solely of nullary data constructors (such as data MyType = A | B | C | D | E | f), convert it to and from MySerializeInt, where MySerializeInt is a constructor for MySerialize that takes one int parameter.I started out with instance (GMySerialize a, GMySerialize b) => GMySerialize (a :+: b) where gtoMyS (L1 x) = MySerializeInt (0 + rest) where rest = case gtoMyS x of MySerializeInt n -> n MySerializeNil -> 0 err -> error $ show err gtoMyS (R1 x) = MySerializeInt (1 + rest) where rest = case gtoMyS x of MySerializeInt n -> n MySerializeNil -> 0 err -> error $ show errBut realised that's horribly wrong, and am not sure how to fix it. How is it wrong? As an example, the following produce the same integer, but they should not as they represent different constructors:M1 {unM1 = L1 (R1 (M1 {unM1 = U1}))}M1 {unM1 = R1 (L1 (M1 {unM1 = U1}))}I'm also unsure how I'd go about writing the gfromMyS instances even if I got gtoMyS working.To phrase it another way, what I'm looking to do has an equivalent effect to writing a Template Haskell function that generates:instance MySerialize t where toMyS x = MySerializeInt (toEnum x) fromMyS (MySerializeInt n) -> Just (fromEnum n) fromMyS _ -> NothingFor every single t where t is sum types with only nullary constructors that implement Enum. 解决方案 The trick is to make another class that counts the number of constructors{-# LANGUAGE ScopedTypeVariables #-}{-# LANGUAGE TypeOperators #-}{-# LANGUAGE DefaultSignatures #-}{-# LANGUAGE DeriveGeneric #-}{-# LANGUAGE KindSignatures #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE FlexibleContexts #-}import Data.Functor ((<$>))import Data.Taggedimport GHC.Genericsclass GNumConstructors (f :: * -> *) where -- Is this close enough to CAF to get memoed in the dictionary? gnumConstructors :: Tagged f Intinstance GNumConstructors (M1 C c f) where gnumConstructors = Tagged 1instance (GNumConstructors a, GNumConstructors b) => GNumConstructors (a :+: b) where gnumConstructors = Tagged $ unTagged (gnumConstructors :: Tagged a Int) + unTagged (gnumConstructors :: Tagged b Int)Then you can easily divide up the integers between those on the left side (less than the number of possibilities on the left) and those on the right side (any larger numbers).type MyS = Intclass GMySerialize f where gtoMyS :: f a -> MyS gfromMyS :: MyS -> Maybe (f a)instance (GNumConstructors a, GMySerialize a, GMySerialize b) => GMySerialize (a :+: b) where gtoMyS (L1 l) = gtoMyS l gtoMyS (R1 r) = unTagged (gnumConstructors :: Tagged a Int) + gtoMyS r gfromMyS x = if x < unTagged (gnumConstructors :: Tagged a Int) then L1 <$> gfromMyS x else R1 <$> gfromMyS (x - unTagged (gnumConstructors :: Tagged a Int))Any individual constructor is represented by 0 and we peek straight through metadata.instance GMySerialize U1 where gtoMyS U1 = 0 gfromMyS 0 = Just U1 gfromMyS _ = Nothinginstance GMySerialize f => GMySerialize (M1 i c f) where gtoMyS (M1 a) = gtoMyS a gfromMyS ms = M1 <$> gfromMyS msCombined with a MySerialize class we can flesh out a complete example for MyType and test itclass MySerialize a where toMyS :: a -> MyS fromMyS :: MyS -> Maybe a default toMyS :: (Generic a, GMySerialize (Rep a)) => a -> MyS toMyS a = gtoMyS $ from a default fromMyS :: (Generic a, GMySerialize (Rep a)) => MyS -> Maybe a fromMyS a = to <$> gfromMyS adata MyType = A | B | C | D | E | F deriving (Generic, Show)instance MySerialize MyTypemain = do print . map toMyS $ [A, B, C, D, E, F] print . map (fromMyS :: MyS -> Maybe MyType) $ [-1, 0, 1, 2, 3, 4, 5, 6]A through F are mapped to the numbers 0 through 5. Reading in those numbers reproduces A through F. Trying to read in a number outside that range produces Nothing.[0,1,2,3,4,5][Nothing,Just A,Just B,Just C,Just D,Just E,Just F,Nothing] 这篇关于GHC泛型:如何编写将和类型从/转换为整数的(:+ :)的实现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 1403页,肝出来的..
09-09 01:57