通用型变压器在Haskell [英] Universal type tranformer in Haskell

查看:77
本文介绍了通用型变压器在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 :: [* - >约束条件])(a :: *)(consb :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::/ [* - >约束])(b :: *)
| 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->b

instance FromTo a a where fromTo = id

instance FromTo Int     Double where fromTo = fromIntegral
instance FromTo Int     Float  where fromTo = fromIntegral
instance FromTo Integer Double where fromTo = fromIntegral
instance FromTo Integer Float  where fromTo = fromIntegral

instance FromTo Double Int     where fromTo = round 
instance FromTo Double Integer where fromTo = round 
instance FromTo Float  Int     where fromTo = round 
instance 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->b

instance (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->b

instance 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 1


The 1-st solution could be improved this way (but it's incorrect way):

{-#LANGUAGE MultiParamTypeClasses #-}
{-#LANGUAGE FlexibleInstances #-}


class FromTo a b where
    fromTo:: a->b

instance FromTo a a where fromTo = id

instance Num b => FromTo Int b where
    fromTo x = fromIntegral x

instance Num b => FromTo Integer b where
    fromTo x = fromIntegral x

instance Integral b => FromTo Float b where
    fromTo x = round x

instance Integral b => FromTo Double b where
    fromTo x = round x

But 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 :: *) where

instance ConstrainedBy '[] t
instance (x t, ConstrainedBy xs t) => ConstrainedBy (x ': xs) t

The 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 t 

instance Show t => Show (ConsBy cons t) where
    show (ConsBy t) = "ConsBy " ++ show t

Then, the FromTo class:

class FromTo (consa:: [* -> Constraint]) (a :: *) (consb :: [* -> Constraint]) (b :: *) where
    fromTo :: ConsBy consa a -> ConsBy consb b

I 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
>x
ConsBy 3
>fromTo x :: ConsBy '[Num] Float
ConsBy 3.0

You 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] a
ConsBy 3
>fromTo x :: (Num a, Eq a) => ConsBy '[Num, Eq] a
ConsBy 4

On 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 b

Now 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 x
ConsBy 3
>:t fromTo x
fromTo x
  :: Num b =>
     ConsBy ((':) (* -> Constraint) Num ('[] (* -> Constraint))) b

As 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 x
fromTo x
  :: Num b =>
     ConsBy ((':) (* -> Constraint) Num ('[] (* -> Constraint))) b

这篇关于通用型变压器在Haskell的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆