加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala与Haskell中List [T]和Set [T]上的模式匹配:类型擦除的效

发布时间:2020-12-16 08:48:57 所属栏目:安全 来源:网络整理
导读:Haskell相当于下面的代码会产生正确的答案吗? 可以修复此Scala代码以生成正确的答案吗?如果有,怎么样? object TypeErasurePatternMatchQuestion extends App { val li=List(1,2,3) val ls=List("1","2","3") val si=Set(1,3) val ss=Set("1","3") def wha
Haskell相当于下面的代码会产生正确的答案吗?

可以修复此Scala代码以生成正确的答案吗?如果有,怎么样?

object TypeErasurePatternMatchQuestion extends App {
  val li=List(1,2,3)
  val ls=List("1","2","3")
  val si=Set(1,3)
  val ss=Set("1","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来理解:任何您删除有关类型的所有特定信息,并进一步删除类型Any是编译器对值o的所有知识.这就是为什么从那时起你只能依赖有关类型的运行时信息.

像case o:List [Int]这样的case表达式是使用JVM的特殊运行时机制实例解析的.但是,您遇到的错误行为是由此机制仅考虑第一级类型(List [Int]中的List)并忽略参数(List [Int]中的Int)引起的.这就是为什么它将List [Int]视为等于List [String].此问题称为“Generics Erasure”.

另一方面,Haskell执行完整类型的擦除,这在answer by Ben中得到了很好的解释.

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

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

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","3")))
println(whatIsIt(Set("1","3")))

输出:

List[String]
Set[String]

Haskell对多态性有一种非常不同的方法.最重要的是,它没有子类型多态性(虽然它不是一个弱点),这就是为什么类型切换模式匹配,就像你的例子中简单无关.但是,可以将Scala解决方案从上面转换为Haskell:

{-# 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,3] :: [Int])
  putStrLn $whatIsIt $toDyn (["1","3"] :: [String])
  putStrLn $whatIsIt $toDyn (Data.Set.fromList ["1","3"] :: Set String)

输出:

[Int]
[String]
Set String

但是,我必须大胆地概述这远不是Haskell编程的典型场景.语言的类型系统足以解决极其复杂的问题,同时保持所有类型级别的信息(和安全性). Dynamic仅用于低级库中的特殊情况.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读