隐式转换:比动态类型更强大?

来源:互联网 发布:ebsco期刊论文数据库 编辑:程序博客网 时间:2024/04/29 21:26
本文内容主要来自Implicit Conversions: More Powerful than Dynamic Typing?,我只对其中一部分进行了翻译。
  隐式转换是Scala中实现的一种类似于Haskell type classes的类型系统,它使得Scala这种静态类型的语言具有某些“动态”的特性。下面用具体例子来说明。

案例1给String添加一个reduce方法,其返回值为字符串中大写字母组成的新字符串。
在Scala中,最终效果如下:
Java代码 复制代码
  1. //Scala   
  2. val acronym = "Microsoft Certified Systems Engineer".reduce   
  3. println(acronym)            // MCSE  

  由于String是Java语言内置的类型,现在遇到的问题是:在静态语言中,我们不能在运行时往已经存在的类中动态添加方法。
  在Scala中,我们可以定义一个新的类型,使它具有所需要的方法,然后再顶一个一个从已有类型到新类型的隐式转换。然后Scala编译器将会在幕后实施其魔法。代码如下:
Java代码 复制代码
  1. class MyStr(str:String) {   
  2.     def reduce = str.foldLeft(""){ (s,c) =>   
  3.                      if(c.isUpperCase) s+c else s   
  4.                  }   
  5. }   
  6.   
  7. implicit def str2MyStr(str:String) = new MyStr(str)  


作为对照,我们看看在Ruby中的实现:
Java代码 复制代码
  1. class String   
  2.   def reduce   
  3.     arr = unpack('c*').select { |c| (65..90).include? c }   
  4.     arr.pack 'c*'  
  5.   end   
  6. end   
  7.     
  8. puts 'HyperText Transfer Protocol'.reduce       # HTTP  



案例2一个稍微复杂一点的问题:重载整数类型的<操作符,使得它能与String比较,并且但String的长度小于该整数时返回true,否则返回false
  在Scala中,同样可以通过隐式转换机制来解决该问题。这一次我们使用比上面更简洁的形式:
Java代码 复制代码
  1. //Scala,that's all   
  2. implicit def lessThanOverload(i: Int) = new {   
  3.     def <(str: String) = str.length < i   
  4. }  

  下面我们看看Ruby中如何解决这个问题,一个很自然的想法是玩Fixnum中添加所需的方法:
Ruby代码 复制代码
  1. class Fixnum  
  2.   def <(str)   
  3.     str.size < self  
  4.   end  
  5. end  

  很不幸的是,这个看起来很对的方法是行不通的。每次调用整数的<操作符时都会进入一个死循环并很快导致堆栈溢出。
  我们可以将方法改写成如下以避免这个问题:
Ruby代码 复制代码
  1. class Fixnum  
  2.   def <(str)   
  3.     self >= str.size   
  4.   end  
  5. end  

  这次不用担心堆栈溢出了,但新的问题出现了:
引用
irb(main):006:0> 123 < 'test'
=> true
irb(main):007:0> 123 < 123
=> true

  这个问题的最终Ruby解法是这样的:
Ruby代码 复制代码
  1. class Fixnum  
  2.   alias_method :__old_less_than__, '<'.to_sym   
  3.   def <(target)   
  4.     if target.kind_of? String  
  5.       __old_less_than__ target.size   
  6.     else  
  7.       __old_less_than__ target   
  8.     end  
  9.   end  
  10. end  

  囧,相比Scala,Ruby的解决方法显得即冗长且丑陋。
原创粉丝点击