在List [T]上的模式匹配和在Scala与Haskell中设置[T]:类型擦除的效果 [英] Pattern matching on List[T] and Set[T] in Scala vs. Haskell: effects of type erasure

查看:149
本文介绍了在List [T]上的模式匹配和在Scala与Haskell中设置[T]:类型擦除的效果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码的Haskell等价物是否会产生正确答案?

这个Scala代码可以修复以产生正确答案吗?如果是,如何?

  object TypeErasurePatternMatchQuestion extends App {
val li = List(1,2,3)
val ls =列表(1,2,3)
val si =设置(1,2,3)
val ss =设置(1,2,3 )
def whatIsIt(o:Any)= o match {
case o:List [Int] => List [Int]
case o:List [String] => List [String]
case o:Set [Int] => Set [Int]
case o:Set [String] => set [String]
}

println(whatIsIt(li))
println(whatIsIt(ls))
println(whatIsIt(si))
println(whatIsIt(ss))

}

打印:

  List [Int] 
List [Int]
Set [Int]
Set [Int ]

但我希望它能够打印:

  List [Int] 
List [String]
Set [Int]
Set [String]


解决方案

您必须明白,通过说 o:任何删除关于类型的所有具体信息,并进一步了解类型任何是编译器知道的关于值的所有信息 o 。这就是为什么从这一点上,你只能依赖关于类型的运行时信息。

使用JVM的特殊<$ c $解决了 case o:List [Int] c> instanceof 运行时机制。但是,您遇到的错误行为是由仅考虑一级类型( List > List [Int] )并忽略参数( List [Int] )中的参数( Int )。这就是为什么它把 List [Int] 等同于 List [String] 。这个问题被称为泛型删除。

另一方面,Haskell执行完整类型的擦除,这在由Ben解答



所以两种语言的问题都是一样的:我们需要提供关于类型及其参数的运行时信息。 / p>

在Scala 中,您可以使用反射库来实现该功能,该功能隐式解析信息:

  import reflect.runtime。{universe => (t.tpe <:List [Int]
else if(t.tpe<:List [String]
else else if(t.tpe <:Set [Int]
else if(t.tpe <:Set [String]
else sys.error(Unexpected type)

println(whatIsIt(List(1, (1,2,3)))

输出:

 列表[String] 
Set [String]

Haskell 有一个非常不同的多态性方法。最重要的是,它没有子类型多态性(尽管这不是弱点),这就是为什么类型切换模式与您的示例中匹配的原因简直无关紧要。但是可以将Scala解决方案从上面翻译成Haskell:

  { - #LANGUAGE MultiWayIf,ScopedTypeVariables# - } 
import Data.Dynamic
import Data.Set

whatIsIt :: Dynamic - >字符串
whatIsIt a =
if | Just(_ :: [Int])< - fromDynamic a - > [Int]
| Just(_ :: [String])< - fromDynamic a - > [String]
| Just(_ :: Set Int)< - fromDynamic a - > Set Int
| Just(_ :: Set String)< - fromDynamic a - > 设置字符串
|否则 - >错误Unexpected type

main = do
putStrLn $ whatIsIt $ toDyn([1,2,3] :: [Int])
putStrLn $ whatIsIt $ toDyn([ 1,2,3] :: [String])
putStrLn $ whatIsIt $ toDyn(Data.Set.fromList [1,2,3] :: Set String )

输出:

  [Int] 
[String]
Set String

然而,我必须大胆地概述,这与Haskell编程的典型场景很不相同。该语言的类型系统足够强大,可以解决极其复杂的问题,同时保持所有类型级别的信息(和安全性)。 动态仅用于低级库中的特殊情况。


Would the Haskell equivalent of the code below produce correct answers?

Can this Scala code be fixed to produce correct answers ? If yes, how ?

object TypeErasurePatternMatchQuestion extends App {
  val li=List(1,2,3)
  val ls=List("1","2","3")
  val si=Set(1,2,3)
  val ss=Set("1","2","3")
  def whatIsIt(o:Any)=o match{
    case o:List[Int]    => "List[Int]"
    case o:List[String] => "List[String]"
    case o:Set[Int]     => "Set[Int]"
    case o:Set[String]     => "Set[String]"
  }

  println(whatIsIt(li))
  println(whatIsIt(ls))
  println(whatIsIt(si))
  println(whatIsIt(ss))

}

prints:

List[Int]
List[Int]
Set[Int]
Set[Int]

but I would expect it to print:

List[Int]
List[String]
Set[Int]
Set[String]

解决方案

You must understand that by saying o:Any you erase all the specific information about the type and further on the type Any is all that the compiler knows about value o. That's why from that point on you can only rely on the runtime information about the type.

The case-expressions like case o:List[Int] are resolved using the JVM's special instanceof runtime mechanism. However the buggy behaviour you experience is caused by this mechanism only taking the first-rank type into account (the List in List[Int]) and ignoring the parameters (the Int in List[Int]). That's why it treats List[Int] as equal to List[String]. This issue is known as "Generics Erasure".

Haskell on the other hand performs a complete type erasure, which is well explained in the answer by Ben.

So the problem in both languages is the same: we need to provide a runtime information about the type and its parameters.

In Scala you can achieve that using the "reflection" library, which resolves that information implicitly:

import reflect.runtime.{universe => ru}
def whatIsIt[T](o : T)(implicit t : ru.TypeTag[T]) = 
  if( t.tpe <:< ru.typeOf[List[Int]] ) 
    "List[Int]"
  else if ( t.tpe <:< ru.typeOf[List[String]] ) 
    "List[String]"
  else if ( t.tpe <:< ru.typeOf[Set[Int]] ) 
    "Set[Int]"
  else if ( t.tpe <:< ru.typeOf[Set[String]] ) 
    "Set[String]"
  else sys.error("Unexpected type")  

println(whatIsIt(List("1","2","3")))
println(whatIsIt(Set("1","2","3")))

Output:

List[String]
Set[String]

Haskell has a very different approach to polymorphism. Above all, it does not have subtype polymorphism (it's not a weakness though), that's why the type-switching pattern matches as in your example are simply irrelevant. However it is possible to translate the Scala solution from above into Haskell quite closely:

{-# LANGUAGE MultiWayIf, ScopedTypeVariables #-}
import Data.Dynamic
import Data.Set

whatIsIt :: Dynamic -> String
whatIsIt a = 
  if | Just (_ :: [Int]) <- fromDynamic a -> "[Int]"
     | Just (_ :: [String]) <- fromDynamic a -> "[String]"
     | Just (_ :: Set Int) <- fromDynamic a -> "Set Int"
     | Just (_ :: Set String) <- fromDynamic a -> "Set String"
     | otherwise -> error "Unexpected type"

main = do
  putStrLn $ whatIsIt $ toDyn ([1, 2, 3] :: [Int])
  putStrLn $ whatIsIt $ toDyn (["1", "2", "3"] :: [String])
  putStrLn $ whatIsIt $ toDyn (Data.Set.fromList ["1", "2", "3"] :: Set String)

Output:

[Int]
[String]
Set String

However I must outline boldly that this is far from a typical scenario of Haskell programming. The language's type-system is powerful enough to solve extremely intricate problems while maintaining all the type-level information (and safety). Dynamic is only used in very special cases in low-level libraries.

这篇关于在List [T]上的模式匹配和在Scala与Haskell中设置[T]:类型擦除的效果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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