8

我正在学习 Ruby,并且在打字方面遇到了一个主要的概念问题。请允许我详细说明为什么我不理解范式。

假设我是像在 Ruby 中那样使用简洁代码的方法链。我必须准确知道链中每个方法调用的返回类型是什么,否则我无法知道下一个链接有哪些方法可用。我每次都必须检查方法文档吗?我遇到了这个不断运行的教程练习。似乎我陷入了一个参考、推断、运行、失败、修复、重复以使代码运行的过程,而不是准确地知道我在编码期间正在使用什么。这与 Ruby 对直观性的承诺背道而驰。

假设我正在使用第三方库,我需要再次知道允许传递参数的类型,否则我会失败。我可以查看代码,但可能有也可能没有任何注释或声明该方法所期望的类型。我了解您基于方法的代码可用于对象,而不是类型。但是我必须确保我作为参数传递的任何内容都具有库所期望的所有方法,所以我仍然需要进行类型检查。我是否必须希望并祈祷所有内容都正确记录在界面上,以便我知道是否需要提供字符串、哈希、类等。

如果我查看方法的来源,我可以获得被调用的方法列表并推断出预期的类型,但我必须执行分析。

红宝石和鸭子打字:合同设计不可能?

前面的stackoverflow问题中的讨论除了“你必须遵循一些流程”之外并没有真正回答任何问题,而且这些流程似乎不是标准的,每个人对遵循什么流程有不同的看法,而且语言有零执行。方法验证?测试驱动设计?记录的 API?严格的方法命名约定?标准是什么,谁规定的?我要遵循什么?这些指南会解决这个问题吗https://stackoverflow.com/questions/616037/ruby-coding-style-guidelines?有编辑帮忙吗?

从概念上讲,我也没有获得优势。您需要知道调用的任何方法都需要哪些方法,因此无论您在编写任何代码时都在键入。除非您决定记录它,否则您只是没有明确地通知语言或其他任何人。然后,您将被困在运行时而不是编码期间进行所有类型检查。我已经完成了 PHP 和 Python 编程,但我也不懂。

我错过了什么或不理解什么?请帮助我理解这个范例。

4

3 回答 3

4

This is not a Ruby specific problem, it's the same for all dynamically typed languages.

Usually there are no guidelines for how to document this either (and most of the time not really possible). See for instance map in the ruby documentation

map { |item| block } → new_ary
map → Enumerator

What is item, block and new_ary here and how are they related? There's no way to tell unless you know the implementation or can infer it from the name of the function somehow. Specifying the type is also hard since new_ary depends on what block returns, which in turn depends on the type of item, which could be different for each element in the Array.

A lot of times you also stumble across documentation that says that an argument is of type Object, Which again tells you nothing since everything is an Object.

OCaml has a solution for this, it supports structural typing so a function that needs an object with a property foo that's a String will be inferred to be { foo : String } instead of a concrete type. But OCaml is still statically typed.

Worth noting is that this can be a problem in statically typed lanugages too. Scala has very generic methods on collections which leads to type signatures like ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Array[T], B, That]): That for appending two collections.

So most of the time, you will just have to learn this by heart in dynamically typed languages, and perhaps help improve the documentation of libraries you are using.

And this is why I prefer static typing ;)

Edit One thing that might make sense is to do what Scala also does. It doesn't actually show you that type signature for ++ by default, instead it shows ++[B](that: GenTraversableOnce[B]): Array[B] which is not as generic, but probably covers most of the use cases. So for Ruby's map it could have a monomorphic type signature like Array<a> -> (a -> b) -> Array<b>. It's only correct for the cases where the list only contains values of one type and the block only returns elements of one other type, but it's much easier to understand and gives a good overview of what the function does.

于 2013-10-03T17:47:22.170 回答
2

是的,您似乎误解了这个概念。它不能替代静态类型检查。只是不同而已。例如,如果您将对象转换为 json(用于将它们呈现给客户端),则您不必关心对象的实际类型,只要它具有#to_json方法即可。在 Java 中,您必须创建IJsonable接口。在红宝石中不需要开销。

至于知道在哪里传递什么以及返回什么:记住这一点或每次查阅文档。我们都这样做。

就在另一天,我看到有 6 年以上经验的 Rails 程序员在 Twitter 上抱怨他无法记住参数的顺序alias_method:新名称是在前还是在后?

这与 Ruby 对直观性的承诺背道而驰。

并不真地。也许它只是写得不好的图书馆。我敢说,在核心 ruby​​ 中,一切都非常直观。

具有强大 IDE 的静态类型语言在这里有一个小优势,因为它们可以在此处非常快速地向您显示文档。不过,这仍在访问文档。只会更快。

于 2013-10-03T17:41:48.727 回答
2

考虑到强类型语言(C++、Java、C# 等)的设计选择强制对传递给方法的类型和方法返回的类型进行严格声明。这是因为这些语言旨在验证参数是否正确(并且由于这些语言是编译的,因此可以在编译时完成这项工作)。但是有些问题只能在运行时回答,例如 C++ 有 RTTI(运行时类型解释器)来检查和执行类型保证。但作为开发人员,您会在语法、语义和编译器的指导下生成遵循这些类型约束的代码。

Ruby 为您提供了采用动态参数类型和返回动态类型的灵活性。这种自由使您能够编写更多通用代码(阅读 Stepanov 关于 STL 和通用编程),并为您提供一组丰富的自省方法(is_a?、instance_of?、respond_to?、kind_of?、is_array? 等)可以动态使用。Ruby 使您可以编写通用方法,但您也可以显式地强制按合同设计,并通过选择的方式处理合同失败。

是的,将方法链接在一起时需要小心,但学习 Ruby 不仅仅是几个新关键字。Ruby 支持多种范式;您可以编写过程式、面向对象、通用和函数式程序。随着您对 Ruby 的了解,您现在所处的周期会迅速改善。

也许您的担忧源于对强类型语言(C++、Java、C# 等)的偏见。鸭子打字是一种不同的方法。你的想法不同。鸭子类型意味着如果一个对象看起来像 a ,行为像 a ,那么它就是 a 。一切(几乎)都是 Ruby 中的对象,所以一切都是多态的。

考虑模板(C++ 有模板,C# 有模板,Java 有模板,C 有宏)。您构建一个算法,然后让编译器为您选择的类型生成实例。您不是通过与泛型的合同来进行设计,但是当您认识到它们的强大功能时,您编写的代码就会减少,并产生更多的东西。

你的一些其他顾虑,

  • 第三方库(宝石)并不像您担心的那样难以使用
  • 记录的 API?请参阅 Rdoc 和http://www.ruby-doc.org/
  • (通常)为库提供 Rdoc 文档
  • 编码指南 - 为初学者查看几个简单 gem 的源代码
  • 命名约定 - 蛇纹和骆驼纹都很流行

建议 - 以开放的心态接近在线教程,做教程(http://rubymonk.com/learning/books/很好),你会有更集中的问题。

于 2013-10-03T18:10:17.810 回答