假设我们要编写一个定义具有某些类型成员或方法的匿名类的宏,然后创建该类的实例,该类通过这些方法静态地键入为结构类型等.这可以通过2.10中的宏系统实现. 0,类型成员部分非常容易:
object MacroExample extends ReflectionUtils {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def foo(name: String): Any = macro foo_impl
def foo_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
}
Run Code Online (Sandbox Code Playgroud)
(提供我方法ReflectionUtils
的便利特性在哪里constructor
.)
这个宏允许我们将匿名类的类型成员的名称指定为字符串文字:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Run Code Online (Sandbox Code Playgroud)
请注意,它是适当的键入.我们可以确认一切都按预期工作:
scala> implicitly[res0.T =:= Int] …
Run Code Online (Sandbox Code Playgroud) 维基百科曾经说*关于鸭子打字:
在使用面向对象编程语言的计算机编程中,duck typing是一种动态类型,其中对象的当前方法和属性集确定有效语义,而不是从特定类或特定接口的实现继承.
(*Ed.注意:自从发布此问题以来,维基百科文章已经过编辑,删除了"动态"一词.)
它说关于结构类型:
结构类型系统(或基于属性的类型系统)是类型系统的主要类,其中类型兼容性和等价性由类型的结构决定,而不是通过显式声明.
它将结构子类型与鸭子类型进行对比,如下所示:
[结构系统]与... duck typing相反,其中仅检查在运行时访问的结构的一部分的兼容性.
然而,术语鸭子打字在我看来至少是直观地包含结构子打字系统.实际上维基百科说:
这个概念的名称[鸭子打字]是指鸭子测试,归功于James Whitcomb Riley,其措辞可能如下:"当我看到一只鸟像鸭子一样散步,像鸭子一样游动,像鸭子一样呱呱叫,我称这只鸟为鸭子."
所以我的问题是:为什么我不能将结构子类型称为鸭子打字?是否存在动态类型语言,这些语言也不能被归类为鸭子类型?
后记:
正如有人叫daydreamdrunk上reddit.com如此雄辩地提出,它 "如果它编译像鸭子,像鸭子链接..."
后后记
许多答案似乎基本上只是重复我已经在这里引用的内容,而没有解决更深层次的问题,这就是为什么不使用术语duck-typing来涵盖动态类型和结构子类型?如果您只想谈论鸭子类型而不是结构子类型,那么只需调用它:动态成员查找.我的问题是没有关于鸭子打字这个术语对我说,这只适用于动态语言.
type-systems duck-typing language-design definitions structural-typing
我在一些代码中需要一些递归结构类型,使用特征和结构类型作为类型参数约束.它工作正常,但后来我了解到Scala不支持递归结构类型.
那么有人可以解释为什么这样可行:
scala> trait Test[M[A] <: { def map[B](f: A => B) : M[B] } ] {}
defined trait Test
Run Code Online (Sandbox Code Playgroud)
而这不是:
scala> def test[M[A] <: { def map[B](f: A => B) : M[B] } ] = null
<console>:5: error: illegal cyclic reference involving type M
def test[M[A] <: { def map[B](f: A => B) : M[B] } ] = null
Run Code Online (Sandbox Code Playgroud) 为什么打印wtf?模式匹配不适用于结构类型吗?
"hello" match {
case s: { def doesNotExist(i: Int, x: List[_]): Double } => println("wtf?")
case _ => println("okie dokie")
}
Run Code Online (Sandbox Code Playgroud) 在Scala中,我可以定义结构类型如下:
type Pressable = { def press(): Unit }
这意味着我可以定义一个函数或方法,它将一个可压缩的参数作为参数,如下所示:
def foo(i: Pressable) { // etc.
我传递给这个函数的对象必须为它定义一个名为press()的方法,该方法匹配类型中定义的类型签名 - 不带参数,返回Unit(Scala的void版本).
我甚至可以使用结构类型内联:
def foo(i: { def press(): Unit }) { // etc.
它基本上允许程序员拥有鸭子类型的所有好处,同时仍然具有编译时类型检查的好处.
C#有类似的东西吗?我用谷歌搜索但找不到任何东西,但我不熟悉C#的任何深度.如果没有,有没有计划添加这个?
如果函数接受结构类型,则可以定义为:
def doTheThings(duck: { def walk; def quack }) { duck.quack }
Run Code Online (Sandbox Code Playgroud)
要么
type DuckType = { def walk; def quack }
def doTheThings(duck: DuckType) { duck.quack }
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过以下方式使用该功能:
class Dog {
def walk { println("Dog walk") }
def quack { println("Dog quacks") }
}
def main(args: Array[String]) {
doTheThings(new Dog);
}
Run Code Online (Sandbox Code Playgroud)
如果您为我的示例反编译(到Java)scalac生成的类,您可以看到该参数doTheThings
是类型的Object
,并且实现使用反射来调用参数上的方法(即duck.quack
)
我的问题是为何反思?是不是可以只使用匿名和invokevirtual而不是反射?
下面是方法来翻译(执行)的结构型呼叫我的例子(Java语法,但问题是字节码):
class DuckyDogTest {
interface DuckType {
void walk();
void quack();
}
static void doTheThing(DuckType d) {
d.quack(); …
Run Code Online (Sandbox Code Playgroud) 我有两个对象,ObjectA和ObjectB,都有方法update().我想编写一个接受ObjectA或ObjectB(但没有其他类型)的函数.从概念上讲,这就是我要做的事情:
def doSomething[T <: ObjectA | T <: ObjectB](obj: T) = {
obj.update
}
Run Code Online (Sandbox Code Playgroud)
我意识到还有其他方法可以解决这个问题(例如,update()方法的结构类型,公共基类等)但我的问题是它可以在Scala中这样做,如果是这样,语法是什么?这叫什么?
类型类和抽象数据类型之间有什么区别?
我意识到这对于Haskell程序员来说是一个基本的东西,但我来自Scala背景,并且会对Scala中的示例感兴趣.我现在能找到的最好的是类型类是"开放的"而ADT是"封闭的".将类型类与结构类型进行比较和对比也是有帮助的.
haskell scala abstract-data-type typeclass structural-typing
这不能按预期工作(因为我试图run
从外部调用包私有Services
):
object Services {
class HelloPrinter {
private[Services] def run = "Hello"
}
}
val obj = new Services.HelloPrinter
Run Code Online (Sandbox Code Playgroud)
但是,令人惊讶的是这有效:
val obj: {def run: String} = new Services.HelloPrinter
obj.run
Run Code Online (Sandbox Code Playgroud)
我会说,它是编译器中的一个错误,因为HelloPrinter由于包可见性规则而与结构类型不匹配,所以它根本不应该编译!
这是程序编译但抛出运行时异常(java.lang.NoSuchMethodException
)的情况:
class HelloPrinter {
private[HelloPrinter] def run = "Hello"
}
val obj: {def run: String} = new HelloPrinter
obj.run
Run Code Online (Sandbox Code Playgroud)
这是我缺少的语言功能或规则,还是合法的Scala中的错误?
reflection scala access-modifiers nosuchmethoderror structural-typing
scala ×8
c# ×2
duck-typing ×2
type-systems ×2
definitions ×1
haskell ×1
macros ×1
reflection ×1
scala-2.10 ×1
scala-macros ×1
typeclass ×1
types ×1
typescript ×1