Scala 中的动态属性

发布于 2024-10-25 04:34:59 字数 683 浏览 2 评论 0原文

Scala 支持动态属性之类的东西吗?示例:

val dog = new Dynamic // Dynamic does not define 'name' nor 'speak'.
dog.name = "Rex" // New property.
dog.speak = { "woof" } // New method.

val cat = new Dynamic
cat.name = "Fluffy"
cat.speak = { "meow" }

val rock = new Dynamic
rock.name = "Topaz"
// rock doesn't speak.

def test(val animal: Any) = {
   animal.name + " is telling " + animal.speak()
}

test(dog) // "Rex is telling woof"
test(cat) // "Fluffy is telling meow"
test(rock) // "Topaz is telling null"

我们在 Scala 中能得到的最接近的东西是什么?如果有像“addProperty”这样的东西允许像普通字段一样使用添加的属性,那就足够了。

我对结构类型声明(“类型安全鸭子类型”)不感兴趣。我真正需要的是在运行时添加新的属性和方法,以便该对象可以由期望添加的元素存在的方法/代码使用。

Does Scala support something like dynamic properties? Example:

val dog = new Dynamic // Dynamic does not define 'name' nor 'speak'.
dog.name = "Rex" // New property.
dog.speak = { "woof" } // New method.

val cat = new Dynamic
cat.name = "Fluffy"
cat.speak = { "meow" }

val rock = new Dynamic
rock.name = "Topaz"
// rock doesn't speak.

def test(val animal: Any) = {
   animal.name + " is telling " + animal.speak()
}

test(dog) // "Rex is telling woof"
test(cat) // "Fluffy is telling meow"
test(rock) // "Topaz is telling null"

What is the closest thing from it we can get in Scala? If there's something like "addProperty" which allows using the added property like an ordinary field, it would be sufficient.

I'm not interested in structural type declarations ("type safe duck typing"). What I really need is to add new properties and methods at runtime, so that the object can be used by a method/code that expects the added elements to exist.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

极致的悲 2024-11-01 04:34:59

Scala 2.9 将有一个经过特殊处理的 Dynamic 特征,这可能正是您正在寻找的。

这个博客有很多关于它的内容:http://squirrelsewer.blogspot.com/2011/ 02/scalas-upcoming-dynamic-capability.html

我猜想在invokeDynamic方法中你需要检查“name_=”、“speak_=”、“name”和“speak”,你可以将值存储在私有映射中。

Scala 2.9 will have a specially handled Dynamic trait that may be what you are looking for.

This blog has a big about it: http://squirrelsewer.blogspot.com/2011/02/scalas-upcoming-dynamic-capabilities.html

I would guess that in the invokeDynamic method you will need to check for "name_=", "speak_=", "name" and "speak", and you could store values in a private map.

梦幻之岛 2024-11-01 04:34:59

我想不出真正需要在运行时动态添加/创建方法/属性的原因除非也允许动态标识符 - 和/或 - 神奇的绑定到外部动态源(JRuby 或 JSON 是两个很好的例子)。

否则,发布的示例可以通过“匿名”类型和结构类型完全使用 Scala 中现有的静态类型来实现。不管怎样,并不是说“动态”不方便(正如 0__ 指出的那样,即将到来——随意“走边缘”;-)。

考虑一下:

val dog = new {
   val name = "Rex"
   def speak = { "woof" }
}

val cat = new {
   val name = "Fluffy"
   def speak = { "meow" }
}

// Rock not shown here -- because it doesn't speak it won't compile
// with the following unless it stubs in. In both cases it's an error:
// the issue is when/where the error occurs.

def test(animal: { val name: String; def speak: String }) = {
   animal.name + " is telling " + animal.speak
}

// However, we can take in the more general type { val name: String } and try to
// invoke the possibly non-existent property, albeit in a hackish sort of way.
// Unfortunately pattern matching does not work with structural types AFAIK :(

val rock = new {
   val name = "Topaz"
}

def test2(animal: { val name: String }) = {
   animal.name + " is telling " + (try {
       animal.asInstanceOf[{ def speak: String }).speak
   } catch { case _ => "{very silently}" })
}

test(dog)
test(cat)
// test(rock) -- no! will not compile (a good thing)
test2(dog)
test2(cat)
test2(rock)

但是,这种方法很快就会变得很麻烦(要“添加”新属性,需要创建一种新类型并将当前数据复制到其中),并且部分利用了示例代码的简单性。也就是说,实际上不可能以这种方式创建真正的“开放”对象;对于“开放”数据,某种 Map 可能是当前 Scala (2.8) 实现中更好/可行的方法。

快乐编码。

I can not think of a reason to really need to add/create methods/properties dynamically at run-time unless dynamic identifiers are also allowed -and/or- a magical binding to an external dynamic source (JRuby or JSON are two good examples).

Otherwise the example posted can be implemented entirely using the existing static typing in Scala via "anonymous" types and structural typing. Anyway, not saying that "dynamic" wouldn't be convenient (and as 0__ pointed out, is coming -- feel free to "go edge" ;-).

Consider:

val dog = new {
   val name = "Rex"
   def speak = { "woof" }
}

val cat = new {
   val name = "Fluffy"
   def speak = { "meow" }
}

// Rock not shown here -- because it doesn't speak it won't compile
// with the following unless it stubs in. In both cases it's an error:
// the issue is when/where the error occurs.

def test(animal: { val name: String; def speak: String }) = {
   animal.name + " is telling " + animal.speak
}

// However, we can take in the more general type { val name: String } and try to
// invoke the possibly non-existent property, albeit in a hackish sort of way.
// Unfortunately pattern matching does not work with structural types AFAIK :(

val rock = new {
   val name = "Topaz"
}

def test2(animal: { val name: String }) = {
   animal.name + " is telling " + (try {
       animal.asInstanceOf[{ def speak: String }).speak
   } catch { case _ => "{very silently}" })
}

test(dog)
test(cat)
// test(rock) -- no! will not compile (a good thing)
test2(dog)
test2(cat)
test2(rock)

However, this method can quickly get cumbersome (to "add" a new attribute one would need to create a new type and copy over the current data into it) and is partially exploiting the simplicity of the example code. That is, it's not practically possible to create true "open" objects this way; in the case for "open" data a Map of sorts is likely a better/feasible approach in the current Scala (2.8) implementation.

Happy coding.

一桥轻雨一伞开 2024-11-01 04:34:59

首先,正如 @pst 指出的,您的示例可以完全使用静态类型来实现,它不需要动态类型。

其次,如果您想使用动态类型语言进行编程,就使用动态类型语言进行编程。

话虽这么说,您实际上可以在 Scala 中做类似的事情。这是一个简单的例子:

class Dict[V](args: (String, V)*) extends Dynamic {
  import scala.collection.mutable.Map

  private val backingStore = Map[String, V](args:_*)

  def typed[T] = throw new UnsupportedOperationException()

  def applyDynamic(name: String)(args: Any*) = {
    val k = if (name.endsWith("_=")) name.dropRight(2) else name
    if (name.endsWith("_=")) backingStore(k) = args.first.asInstanceOf[V]
    backingStore.get(k)
  }

  override def toString() = "Dict(" + backingStore.mkString(", ") + ")"
}

object Dict {
  def apply[V](args: (String, V)*) = new Dict(args:_*)
}

val t1 = Dict[Any]()
t1.bar_=("quux")

val t2 = new Dict("foo" -> "bar", "baz" -> "quux")
val t3 = Dict("foo" -> "bar", "baz" -> "quux")

t1.bar // => Some(quux)
t2.baz // => Some(quux)
t3.baz // => Some(quux)

正如您所看到的,实际上您已经非常接近了。你的主要错误是 Dynamic 是一个特征,而不是一个类,所以你不能实例化它,你必须将它混合进去。而且你显然必须实际定义你想要它做什么,即实现typedapplyDynamic

如果您希望您的示例能够正常工作,那么会遇到一些复杂的情况。特别是,您需要诸如类型安全的异构映射之类的东西作为后备存储。此外,还有一些语法方面的考虑。例如,如果 foo.bar_= 存在,则 foo.bar = baz 仅翻译为 foo.bar_=(baz),但事实并非如此不是,因为 foo 是一个 Dynamic 对象。

First off, as @pst pointed out, your example can be entirely implemented using static typing, it doesn't require dynamic typing.

Secondly, if you want to program in a dynamically typed language, program in a dynamically typed language.

That being said, you can actually do something like that in Scala. Here is a simplistic example:

class Dict[V](args: (String, V)*) extends Dynamic {
  import scala.collection.mutable.Map

  private val backingStore = Map[String, V](args:_*)

  def typed[T] = throw new UnsupportedOperationException()

  def applyDynamic(name: String)(args: Any*) = {
    val k = if (name.endsWith("_=")) name.dropRight(2) else name
    if (name.endsWith("_=")) backingStore(k) = args.first.asInstanceOf[V]
    backingStore.get(k)
  }

  override def toString() = "Dict(" + backingStore.mkString(", ") + ")"
}

object Dict {
  def apply[V](args: (String, V)*) = new Dict(args:_*)
}

val t1 = Dict[Any]()
t1.bar_=("quux")

val t2 = new Dict("foo" -> "bar", "baz" -> "quux")
val t3 = Dict("foo" -> "bar", "baz" -> "quux")

t1.bar // => Some(quux)
t2.baz // => Some(quux)
t3.baz // => Some(quux)

As you can see, you were pretty close, actually. Your main mistake was that Dynamic is a trait, not a class, so you can't instantiate it, you have to mix it in. And you obviously have to actually define what you want it to do, i.e. implement typed and applyDynamic.

If you want your example to work, there are a couple of complications. In particular, you need something like a type-safe heterogenous map as a backing store. Also, there are some syntactic considerations. For example, foo.bar = baz is only translated into foo.bar_=(baz) if foo.bar_= exists, which it doesn't, because foo is a Dynamic object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文