我正在学习Ruby,而且我有一个关于打字的重大概念问题。请允许我详细说明为什么我不理解范式。
假设我在Ruby中使用简洁代码链接方法。我必须准确地知道链中每个方法调用的返回类型,否则我不知道下一个链接上有哪些方法可用。我每次都要检查方法文档吗?我正在遇到这个不断运行的教程练习。我似乎陷入了一个引用过程,推断,运行,失败,修复,重复以使代码运行,而不是确切地知道我在编码期间正在使用什么。面对Ruby对直觉性的承诺,这种情况很快。
假设我正在使用第三方库,我需要知道哪些类型允许传递参数,否则我会失败。我可以查看代码,但可能有也可能没有任何评论或声明该方法所期望的类型。我理解您基于方法的代码可用于对象,而不是类型。但是我必须确定我传递的任何参数都包含库所期望的所有方法,所以我仍然需要进行类型检查。我是否必须希望并祈祷在界面上正确记录所有内容,以便我知道我是否应该提供字符串,哈希,类等等。
如果我查看方法的来源,我可以获得一个被调用的方法列表并推断出预期的类型,但我必须执行分析。
Ruby and duck typing: design by contract impossible?
前面的stackoverflow问题中的讨论并没有真正回答除了“你必须遵循的流程”以外的任何内容,而且这些流程似乎不是标准的,每个人对要遵循的流程有不同的看法,该语言没有强制执行。方法验证?测试驱动设计?记录的API?严格的方法命名约定?什么是标准,谁来决定它?我该怎么做?这些指南是否会解决这一问题https://stackoverflow.com/questions/616037/ruby-coding-style-guidelines?编辑有帮助吗?
从概念上讲,我也没有获得优势。您需要知道所调用的任何方法需要哪些方法,因此无论您在编写任何代码时都在键入。除非您决定记录,否则您不会明确地通知该语言或其他任何人。然后,您在运行时而不是在编码期间进行所有类型检查。我已经完成了PHP和Python编程,我也不了解它。
我缺少什么或不理解?请帮助我理解这个范例。
答案 0 :(得分:4)
这不是Ruby特有的问题,对于所有动态类型的语言都是一样的。
通常没有关于如何记录这一点的指南(并且大部分时间都不可能)。请参阅ruby文档中的map
map { |item| block } → new_ary
map → Enumerator
这里的item
,block
和new_ary
是什么?它们有何关联?除非你知道实现或者可以以某种方式从函数的名称推断它,否则无法判断。指定类型也很困难,因为new_ary
取决于block
返回的内容,而item
又取决于Object
的类型,对于数组中的每个元素,foo
的类型可能不同。
很多时候,您还会偶然发现一个文档,说明参数类型为String
,因为一切都是对象,所以它再也没有告诉您。
OCaml有一个解决方案,它支持结构类型,因此需要一个具有属性{ foo : String }
且++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Array[T], B, That]): That
的对象的函数将被推断为++
而不是具体的类型。但OCaml仍然是静态类型。
值得注意的是,这也可能是静态类型语言中的问题。 Scala在集合上有非常通用的方法,它们会导致像++[B](that: GenTraversableOnce[B]): Array[B]
这样的类型签名,用于附加两个集合。
因此,大多数情况下,您只需要在动态类型语言中学习这一点,并且可能有助于改进您正在使用的库的文档。
这就是我喜欢静态输入的原因;)
编辑可能有意义的一件事就是做Scala也做的事情。它默认情况下并不显示Array<a> -> (a -> b) -> Array<b>
的类型签名,而是显示{{1}},它不是通用的,但可能涵盖大多数用例。因此对于Ruby的地图,它可以具有像{{1}}这样的单形类型签名。它只适用于列表只包含一种类型的值并且块只返回另一种类型的元素的情况,但它更容易理解,并且可以很好地概述函数的作用。
答案 1 :(得分:2)
考虑强类型语言(C ++,Java,C#等)的设计选择强制执行传递给方法的类型的严格声明,以及方法返回的类型。这是因为这些语言旨在验证参数是否正确(并且由于编译了这些语言,因此可以在编译时完成此工作)。但有些问题只能在运行时得到解答,例如C ++有RTTI(运行时类型解释器)来检查和实施类型保证。但作为开发人员,您将受到语法,语义和编译器的指导,以生成遵循这些类型约束的代码。
Ruby使您可以灵活地获取动态参数类型,并返回动态类型。这种自由使您能够编写更多通用代码(在STL和泛型编程上阅读Stepanov),并为您提供丰富的内省方法(is_a?,instance_of?,respond_to?,kind_of?,is_array?等)。可以动态使用。 Ruby允许您编写泛型方法,但您也可以通过合同明确强制执行设计,并通过选择的方式处理合同失败。
是的,在将方法链接在一起时需要小心,但学习Ruby不仅仅是一些新的关键词。 Ruby支持多种范式;你可以编写程序,对象oriend,泛型和功能程序。当您了解Ruby时,您现在所处的周期将会迅速改善。
也许你的担忧源于对强类型语言的偏见(C ++,Java,C#等)。鸭子打字是一种不同的方法。你的想法不同。鸭子打字意味着如果一个物体看起来像一个,表现得像一个,那么它就是一个。一切(差不多)都是Ruby中的一个对象,所以一切都是多态的。
考虑模板(C ++有它们,C#有它们,Java有它们,C有宏)。您构建算法,然后让编译器为您选择的类型生成实例。你不是通过与泛型合同进行设计,但是当你认识到它们的力量时,你就会编写更少的代码并生成更多代码。
您还有一些其他问题,
建议 - 以开放的心态学习在线教程,做教程(http://rubymonk.com/learning/books/很好),你会有更多专注的问题。
答案 2 :(得分:1)
是的,你似乎误解了这个概念。它不是静态类型检查的替代品。它只是不同。例如,如果将对象转换为json(用于将它们呈现给客户端),则只要它具有#to_json
方法,就不关心对象的实际类型。在Java中,您必须创建IJsonable
接口。在红宝石中不需要任何开销。
至于知道什么传递在哪里以及什么返回什么:每次记住这个或咨询文档。我们都这样做。
就在另一天,我看到有超过6年经验的铁道程序员在推特上抱怨他无法记住alias_method
的参数顺序:新名称是第一个还是最后一个?
面对Ruby对直觉性的承诺,这种情况很快。
不是真的。也许它只是写得很糟糕的库。在核心红宝石中,我敢说,一切都非常直观。
具有强大IDE的静态类型语言在这里有一个小优势,因为它们可以非常快速地向您显示文档。不过,这仍然是访问文档。只有更快。