本文介绍了通用型变压器在Haskell的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 从逻辑上讲,可以定义通用转换函数,它可以从任何类型转换为任何类型。 可能的方法是: $ $ p $ {$#$> $ - #LANGUAGE MultiParamTypeClasses# - } { - #LANGUAGE FlexibleInstances# - } class FromTo ab其中 fromTo :: a-> b 实例FromTo aa fromTo = id 实例FromTo Int Int Double从fromTo = fromIntegral 实例FromTo Int Float其中fromTo = fromIntegral 实例FromTo整数Double其中fromTo = fromIntegral 实例FromTo整数Float fromTo = fromIntegral 实例FromTo Double Int其中fromToTo = round 实例FromTo Double Integer其中fromTo = round 实例FromTo Float Int其中fromTo = round 实例FromTo Float Integer where from = = - 等等 好的,它的功能是可扩展的。但它非常笨重,因为我必须列出我想要使用的任何案例。 有没有解决这个问题的好方法? 如果它是正确的(但它不是),整洁的一个解决方案可以像这样完成: / b> { - #LANGUAGE MultiParamTypeClasses# - } { - #LANGUAGE FlexibleInstances# - } { - # LANGUAGE InstanceSigs# - } class FromTo ab其中 fromTo :: a-> b 实例(Integral a,Num b)=> FromTo ab其中 fromTo :: a-> b fromTo x =(fromIntegral x) {---评论,因为加法中断程序.----- -------------------------- 实例(RealFrac a,Integral b)=> FromTo ab其中 fromTo :: a-> b fromTo x =(round x) - } 如果有一些类型集扩展(类似Haskell的伪代码),也许会有可能: { - #语言MultiParamTypeClasses# - } { - #LANGUAGE FlexibleInstances# - } { - #LANGUAGE InstanceSigs# - } { - #LANGUAGE TypeSets# - } class FromTo ab其中 fromTo :: a-> b 实例setfrom(积分a,数字b)。 (积分a,数字b)=> FromTo a b其中 fromTo :: a-> b fromTo x =(fromIntegral x) 实例setfrom(RealFrac a,Integral b)。 (RealFrac a,Integral b)=> FromTo ab其中 fromTo :: a-> b fromTo x =(round x) setfrom C1 a。这里应该定义一组类型,使用来自 C1 class的实例信息。编译器应该检查实例是否相交。这个扩展的其他可能的结构是 set(T1,T2,...,TN)a。,它允许仅仅定义类型集。 UPD 1 1-st解决方案可以通过这种方式进行改进(但它是不正确的方式): { - #语言MultiParamTypeClasses# - } { - #LANGUAGE FlexibleInstances# - } 类FromTo ab其中 fromTo :: a-> b 实例FromTo aa其中fromTo = id 实例Num b => FromTo Int b其中 fromTo x = fromIntegral x 实例Num b => FromTo整数b其中 fromTo x = fromIntegral x 实例积分b => FromTo Float b where fromTo x = round x instance Integral b => FromTo Double b where fromTo x = round x 但它仍然不好, * Main>>在交互模式下调用时会产生重叠: fromTo(10 :: Double):: Double < interactive>:108:1:从FromTo Double Double的重叠实例使用`fromTo'匹配实例:实例FromTo aa - 定义在4.hs:8:10 实例Integral b => FromTo Double b - 定义为4.hs:19:10 在表达式中:fromTo(10 :: Double):: Double 在等式中:it = fromTo(10: :Double):: Double 解决方案你想通过类型的约束来参数化类实例。这可以通过现代的GHC扩展来实现: $ {$#$ $ $ $ $ $ $ $ $ $ $ $ {$# DataGrids,TypeOperators,UndecidableInstances,GADTs# - } import GHC.Prim(Constraint) class ConstrainedBy(cons :: [* - > Constraint] )(t :: *)其中 实例ConstrainedBy'[] t 实例(xt,ConstrainedBy xs t)=> ConstrainedBy(x':xs)t 这个类的目的是允许单个约束键入 FromTo 类。例如,您可以决定 Num a,Real a =>浮动的实例不同于 Num a =>浮动一个(这是一个人为的例子 - 但取决于你的用例,你可能需要这个功能)。 现在我们用GADT将这个类提升到数据级别: data ConsBy cons t where ConsBy :: ConstrainedBy cons t => t - > ConsBy cons t 实例显示t =>显示(ConsBy cons t)其中显示(ConsBy t)=ConsBy++显示t 然后, FromTo 类: class FromTo(consa :: [* - >约束])(a :: *)(consb :: [* - >约束])(b :: *)其中 fromTo :: ConsBy consa a - > ConsBy consb b 我不认为有办法让您指定的类型函数 fromTo ;如果该类型仅仅是 a - > b ,没有办法从函数参数中推导出约束条件。 您的实例: instance(积分a,数字b )=> FromTo'[Integral] a'[Num] b其中 fromTo(ConsBy x)= ConsBy(来自整数x) 实例(RealFrac a,Integral b)=> FromTo'[RealFrac] a'[Integral] b其中 fromTo(ConsBy x)= ConsBy(round x) 不幸的是,你必须两次陈述所有的约束条件。然后: > let x = ConsBy 3 :: Integral a => ConsBy'[Integral] a > x ConsBy 3 > fromTo x :: ConsBy'[Num] Float ConsBy 3.0 您可以将实例视为重叠: 实例(积分a,等式b,数值b)=> FromTo'[Integral] a'[Num,Eq] b其中 fromTo(ConsBy x)= ConsBy(fromIntegral x + 1) - 明显愚蠢 > let x = ConsBy 3 :: Integral a => ConsBy'[Integral] a > fromTo x :: Num a => ConsBy'[Num] a ConsBy 3 > fromTo x ::(Num a,Eq a)=> ConsBy'[Num,Eq] a ConsBy 4 另一方面,如果你希望声明只有一个实例可以匹配类型和约束的组合(使上述不可能),您可以使用函数依赖来执行此操作: | consa - > consb b,consb b - >包括,其中来自:: ConsBy consa a - > ConsBy consb b 现在我写的第三个实例是无效的,但是,您可以使用 from 没有明确的类型注释: > let x = ConsBy 3 :: Integral a => ConsBy'[Integral] a > fromTo x ConsBy 3 >:t fromTo x fromTo x :: Num b => ConsBy((':)(* - > Constraint)Num('[](* - > Constraint)))b 如您所见,输出类型 Num b => b ,是从输入类型推断出来的。这对于多态和具体类型都是一样的: > let x = ConsBy 3 :: ConsBy'[Integral] Int >:t fromTo x fromTo x :: Num b => ConsBy((':)(* - > Constraint)Num('[](* - > Constraint)))b Logically, it's possible to define universal transformation function, that can transform from any type to any type.The possible way is:{-#LANGUAGE MultiParamTypeClasses #-}{-#LANGUAGE FlexibleInstances #-}class FromTo a b where fromTo:: a->binstance FromTo a a where fromTo = idinstance FromTo Int Double where fromTo = fromIntegralinstance FromTo Int Float where fromTo = fromIntegralinstance FromTo Integer Double where fromTo = fromIntegralinstance FromTo Integer Float where fromTo = fromIntegralinstance FromTo Double Int where fromTo = roundinstance FromTo Double Integer where fromTo = roundinstance FromTo Float Int where fromTo = roundinstance FromTo Float Integer where fromTo = round-- e.t.c.Well, it work's, it's extendable. But it's very bulky, because I must list any case I want to use.Is there any good solutions for this?The neat one solution could be done like this, if it was correct (but it's not):{-#LANGUAGE MultiParamTypeClasses #-}{-#LANGUAGE FlexibleInstances #-}{-#LANGUAGE InstanceSigs #-}class FromTo a b where fromTo:: a->binstance (Integral a, Num b) => FromTo a b where fromTo::a->b fromTo x = (fromIntegral x){---Commented, because addition breaks program.-------------------------------instance (RealFrac a, Integral b) => FromTo a b where fromTo::a->b fromTo x = (round x)-}Maybe it would be possible if there was some type sets extension (Haskell-like pseudo code):{-#LANGUAGE MultiParamTypeClasses #-}{-#LANGUAGE FlexibleInstances #-}{-#LANGUAGE InstanceSigs #-}{-#LANGUAGE TypeSets #-}class FromTo a b where fromTo:: a->binstance setfrom (Integral a, Num b). (Integral a, Num b) => FromTo a b where fromTo::a->b fromTo x = (fromIntegral x)instance setfrom (RealFrac a, Integral b). (RealFrac a, Integral b) => FromTo a b where fromTo::a->b fromTo x = (round x)setfrom C1 a. here should define set of types, using instances information from C1 class. The compiler should check if instances are intersects. The other possible construction of this extension is set (T1,T2,...,TN) a., that allows to merely define the type set.UPD 1The 1-st solution could be improved this way (but it's incorrect way):{-#LANGUAGE MultiParamTypeClasses #-}{-#LANGUAGE FlexibleInstances #-}class FromTo a b where fromTo:: a->binstance FromTo a a where fromTo = idinstance Num b => FromTo Int b where fromTo x = fromIntegral xinstance Num b => FromTo Integer b where fromTo x = fromIntegral xinstance Integral b => FromTo Float b where fromTo x = round xinstance Integral b => FromTo Double b where fromTo x = round xBut it's still not good and, in addition, gives overlaps when calling in interactive mode:*Main> fromTo (10::Double) ::Double<interactive>:108:1: Overlapping instances for FromTo Double Double arising from a use of `fromTo' Matching instances: instance FromTo a a -- Defined at 4.hs:8:10 instance Integral b => FromTo Double b -- Defined at 4.hs:19:10 In the expression: fromTo (10 :: Double) :: Double In an equation for `it': it = fromTo (10 :: Double) :: Double 解决方案 From what I understand, you want to parameterize class instances by the constraints on the types. This is possible with modern GHC extensions:{-#LANGUAGE MultiParamTypeClasses, FlexibleInstances, InstanceSigs, ConstraintKinds, KindSignatures, DataKinds, TypeOperators, UndecidableInstances, GADTs #-}import GHC.Prim(Constraint)class ConstrainedBy (cons :: [* -> Constraint]) (t :: *) whereinstance ConstrainedBy '[] tinstance (x t, ConstrainedBy xs t) => ConstrainedBy (x ': xs) tThe purpose of this class is to allow multiple constraints on a single type in the FromTo class. For example, you could decide that Num a, Real a => Floating a has a different instance than Num a => Floating a (this is a contrived example - but depending on your use cases, you may have need for this functionality).Now we 'lift' this class to the data level with a GADT:data ConsBy cons t where ConsBy :: ConstrainedBy cons t => t -> ConsBy cons tinstance Show t => Show (ConsBy cons t) where show (ConsBy t) = "ConsBy " ++ show tThen, the FromTo class:class FromTo (consa:: [* -> Constraint]) (a :: *) (consb :: [* -> Constraint]) (b :: *) where fromTo :: ConsBy consa a -> ConsBy consb bI don't believe that there is a way to have the type that you specified for the function fromTo; if the type is simply a -> b, there is no way to deduce the constraints from the function arguments.And your instances:instance (Integral a, Num b) => FromTo '[Integral] a '[Num] b where fromTo (ConsBy x) = ConsBy (fromIntegral x)instance (RealFrac a, Integral b) => FromTo '[RealFrac] a '[Integral] b where fromTo (ConsBy x) = ConsBy (round x)You have to state all the constraints twice, unfortunately. Then:>let x = ConsBy 3 :: Integral a => ConsBy '[Integral] a>xConsBy 3>fromTo x :: ConsBy '[Num] FloatConsBy 3.0You can have instances that would normally be considered 'overlapping':instance (Integral a, Eq b, Num b) => FromTo '[Integral] a '[Num, Eq] b where fromTo (ConsBy x) = ConsBy (fromIntegral x + 1) -- obviously stupid>let x = ConsBy 3 :: Integral a => ConsBy '[Integral] a>fromTo x :: Num a => ConsBy '[Num] aConsBy 3>fromTo x :: (Num a, Eq a) => ConsBy '[Num, Eq] aConsBy 4On the other hand, if you wish to make the assertion that there is only one instance that can match a combination of type and constraints (making the above impossible), you can use functional dependencies to do this: {-# LANGUAGE FunctionalDependencies #-}class FromTo (consa:: [* -> Constraint]) (a :: *) (consb :: [* -> Constraint]) (b :: *) | consa a -> consb b, consb b -> consa a where fromTo :: ConsBy consa a -> ConsBy consb bNow the third instance that I wrote is invalid, however, you can use fromTo without explicit type annotations: >let x = ConsBy 3 :: Integral a => ConsBy '[Integral] a>fromTo xConsBy 3>:t fromTo xfromTo x :: Num b => ConsBy ((':) (* -> Constraint) Num ('[] (* -> Constraint))) bAs you can see, the output type, Num b => b, is inferred from the input type. This works the same for polymorphic and concrete types:>let x = ConsBy 3 :: ConsBy '[Integral] Int>:t fromTo xfromTo x :: Num b => ConsBy ((':) (* -> Constraint) Num ('[] (* -> Constraint))) b 这篇关于通用型变压器在Haskell的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
09-14 15:00