分享 刚在看一篇讲述 monads 的文章,然后,Ruby 被黑了。。。

Unknow user · April 12, 2014 · Last by karmue replied at April 15, 2014 · 4091 hits

先上图:

原文在这里:Functors, Applicatives, And Monads In Pictures

虽然看不到 haskell,不过感觉好简洁的样子,喜欢。。。

Post.find_by_id(1).try :title

  • -

这不算黑吧,真要比的话 ObjC 也能一行搞定啊

[[Post findByID:1] title]
Unknow user #4 April 12, 2014

@jasl 好技巧, @PrideChung 看来要歪楼了,发帖本意是分享,原文确实是拿来对比以说明 fmap 的特点的。

低端黑 自己写不出来优雅的代码怪语言。

Post.find(1).try(:title)

他那两行 ruby 代码本来也不怎么样。

...楼上的怎么敢拿别人写好的库和语言内置功能比。。。

他那两行 ruby 已经很明白了…… try 的实现就更麻烦了去了……

这个要看场景,很多时候报错优于沉默。

比如作者引用的find_by_id。在大多数场景下,特别是 controller 中,应该用find来期待报错而不是find_by_id,然后rescue ActiveRecord::RecordNotFound

有时候沉默会比较好,比如处理复杂的 hash 的时候。

那哥们明显不懂 Ruby

@5swords

Post.find(1).try(:title)

@Kabie getPostTitle findPost 是 Haskell 语言的原生语法吗?

#10 楼 @swordray getPostTitle 对应的是 post.title……我是在比较 tryMaybe

try 是 Rails 对 NilClassObject 各注入了一个方法……实际的 Ruby 实现原理还是他那两行……

……而 Maybe 是语言的内置类型……

我觉得这种做法明显合理得多:在执行流中不用管是否为空值……只需要在最后看是不是空就行了……

而 Ruby 这种你每一步都得看是不是 nil …… try 了又 try 的代码我也写过不少……不知道 Ruby 有没有更好的模式……

确实 try 是 Rails 对 nil 的扩展,不如内置在语言里,但作者故意把 Ruby 的代码写的丑拿来作比较也不太厚道。

#11 楼 @Kabie Maybe 这种没看出来优雅在哪,喜欢这种不报错又能执行下去的特性推荐上 PHP,只要没有语法错误,php 中间全挂了还能继续执行下去,然后把 display_error 关掉照样当做没发生。

#11 楼 @Kabie 这样的比较方法不太对等,有些语言把很多函数都写进标准库了,而 Ruby 的文化是标准库尽量简洁,移到第三方库让开发者自由选择

#10 楼 @swordray #6 楼 @5swords 直接用 find 有可能报错的呦~ ActiveRecord::RecordNotFound~

Post.find_by_id(1).try :title

月经贴,鉴定完毕。

觉得后半部分似乎可以这么写 post && post.title

#15 楼 @zj0713001 对,忘了作者需求是不抛异常

Post.find_by(id: 1).try(:title)

提示一下 find_by_id 已被 Rails 4 抛弃

还记得那个经典的 nil.id 返回 4 吗? 出问题不抛异常也算特性的话,用自然语言好了。

#20 楼 @kgen 看具体场景了,比如一个页面显示元素如果没有填就留空,不需要抛异常让整个页面报 500

#15 楼 @zj0713001

谢谢,要这样写:Post.where("id = ?",1).first.try(:title)

#11 楼 @Kabie 我写了一个 trytry,象这样:a.trytry('b.c.d.e') haskell 不熟悉,看着语法不是太喜欢,我会选择在 ruby 里做补丁,然后等 matz 把好东西都拿过来。

module Object
  include ModuleBase
  def trytry(sym_list, value=nil)
    return self if value.present? and sym_list.last != '='
    receiver = self
    sym_list.to_s.split('.').each do |field| 
      if field.last == '=' 
        receiver.try(field, value)
        receiver.save!
      else
        receiver = if field =~ /\(/ 
          params = field.gsub(/[ \):]/, '').gsub('(', ',').split(',')
          receiver.try(params[0], *params[1..-1])
        else
          receiver.try(field)
        end
      end
    end
    receiver
  end
end
23 Floor has deleted

@5swords present?try都是 Rails 里面的,你却拿来给 Ruby 打补丁,这都是哪跟哪啊。

另外 try 最好也不要滥用。像这种Post.where("id = ?",1).first.try(:title)在实际中根本用不着。

为什么?如果你是显示 Post 页面,id = 1 的 post 根本就不存在,你也就去不了那个页面,就不存在 try 这 try 那的问题来。

如果你是显示一个 collection, 比如 Post.last(5)。id = 1 的 post 根本就不在这个 collection 当中,更没有必要 try。

Post.find_by_id(id).title rescue nil Ruby 从来都是秒杀其它编程语言的语法

@keating 看来我今天要喷好几次了。无条件 rescue 是大忌中的大忌,绝不可用。

#26 楼 @billy 求详细喷无条件 rescue

@Kabie 主要原因是这个 rescue 把以前所有可能发生的报错全部拯救了,这样调试时根本看不到正常的 backtrace,可能的坏蛋都被这个包庇者藏起来了。

但是有具体 class 的拯救还是好的,比如rescue ActiveRecord::RecordNotFound

总之 resue 应该用于具体的,有意义的拯救。像这种解决显示 title 的问题,即使有具体的拯救也没有必要使用。好比郭靖也不会见人就使降龙十八掌。

更多的你可以看看这个: http://stackoverflow.com/questions/10048173/why-is-it-bad-style-to-rescue-exception-e-in-ruby

Don’t rescue Exception. EVER. or I will stab you.

就这个具体场景而言,我认为find_by_idtry(:title)都是不好的做法。好的做法是先拿到 post instance, 然后再展开各种 attributes。如果找不到,rescue as above。

#24 楼 @billy 谢谢,给 ruby 打补丁的意思就象是 rails 对 ruby 做的一样,我的意思是再加一点。 至于 try 的问题,就是楼主引用文章里的要求,post 为 1 不存在时,取 title 时返回 nil。

@5swords 明白。乱喷了一下,勿怪 :)

#30 楼 @billy 谢谢,真的程序员不怕喷!喷错了与我无关,喷对了我又明白了道理,省了学费。

但是这个例子怎么拿?用 find 找不到就有 exception 了,拿到 post instance 这 title 也不会没有。

haskell 和 ruby 都是函数式,只不过 haskell 有内置 fmap,just,nothing 等,而 ruby 没有,ruby 实现类似的也很简单:

fmap = ->(f, x) do
  x.nil? ? nil : f.(x)
end

getPostTitle = ->(post) { post.title }
findPost = ->(id)  {Post.where(:id => id).first}

fmap.(getPostTitle, findPost.(1)) # return post 1 title
fmap.(getPostTitle, findPost.(0)) # return nil

只不过对于这种需求,ruby 一般不用函数式编程,而是用 #2 楼 @jasl 提到的 rails 扩展的 try 这种声明式编程

我想说实际开发中 Ruby 和 Haskell 的这两段代码有毛用啊~

用 find 不就是希望他能报错吗? 不想报错的用 where 再处理空集的情况

You need to Sign in before reply, if you don't have an account, please Sign up first.