Crystal 说我最近关注 Crystal 的感受

huacnlee · April 13, 2017 · Last by changtimwu replied at June 28, 2017 · 18124 hits
Topic has been selected as the excellent topic by the admin.

用 Crystal 来写东西,看起来像是能用 Ruby 那样的技巧,但实际上你是在写静态语言,有各种类型限制、转换什么的,其实感觉和写 Go 差不多

Immutable

然后 Crystal 里面 HashNamedTuple 比较费神,在 Ruby 里面我们惯用 { foo: 1, bar: 2 } 这样的方式,在到了 Crystal 里面,这种是不可变的,于是有了 Hash(可变)NamedTuple(不可变)的区别

  • { :foo => 1 }, { "foo" => 1 } 这样是 Hash
  • { foo: 1 } 这样声明是 NamedTuple

Crystal 里面,那些例如 downcase! gsub! 这样 Ruby 常用的改变自身,减少内存 Copy 来提升效率的方式,没有了,如同 NamedTuple,String 也 immutable 的,你只需要:

foo = foo.downcase.gsub(/[a]/, "f")

当然,也有好的地方

变量类型声明支持多种并行类型(Union types)

a = nil
a = 1
a = "foobar"
# a is Int | String | Nil

a 就同时是 Int, String, Nil 3 种类型。

这个是用来达到动态语言体验的方式。

所有类型基于 Object

Crystal 如同 Ruby 一样,所有类型都是基于 Object,于是乎:

可以任意类型变量

a.to_s
a.as(String)
a.blank?
a.inspect 

… 甚至可以做各种扩展

可以编写 Bash 脚本

它也可以像动态语言一样,写 Bash 执行脚本,某些场景你可能需要用它:

#! /usr/bin/env crystal
puts "hello world"

然后你就可以动态执行:

$ chmod +x ./hello
$ ./hello

标准库基于 Crystal 实现,而不是 C

Crystal 目前的源代码,大量标准库的实现都是用 Crystal 实现的了,你能很容易看懂,了解原理,找到 Bug,甚至改进它,而 Ruby 里面大量都是 C 的实现。

crystal tool format

内建 Format 工具,帮助你格式化代码,像 go fmt 一样,再也不用担心代码风格的事情。此外,在 Crystal 里面,你不在会纠结 '" 的风格问题,Crystal 里面,单引号用于 Char,String 类型必须用双引号。

内置的 Spec

Crystal 内置 spec 用于编写测试,体验如同 Ruby 一样一样的,很好!


关于性能

大家都容易关注到这点,这也是用它的原因。

官方页面以及各种介绍的地方都在提性能,我的一些感受:

  • 它抛弃了 Ruby 历史包袱,从语言设计上就可以做性能的考虑,例如 String 不可变,去掉了 Ruby 动态 eval 的部分,改用 macro 代替动态特性(编译期执行)等等动作,Ruby 要这么做,应该也能有很大的提升。
  • 目前它只有单线程的方式,靠多进程和 Fiber 的方式来实现并发(Concurrency),像 Go 那样的 Channel 机制,挺好用的。不过依然如同 Ruby 一样单核,也如同 Ruby Python Node.js 一样,这只是对于 IO 的部分有效。
  • Crystal 1.0 的目标是要实现多线程,实现并行(Parallelism),具体可以关注 GitHub crystal-lang/crystal#thread-support 分支,据他们在 Twitter 上说,快搞定了。

例子:

require "http/client"

ch = Channel(Int32).new
10.times do
  spawn do
    res = HTTP::Client.get("https://ruby-china.org")
    ch.send(res.status_code)
  end
end

10.times do
  status_code = ch.receive
  puts "status: #{status_code}"
end

关于编译

我用它写过一个小工具,可能关注过的人都看过,用它写绿色文件的好处。

但实际上没有那么美好,用 Crystal 写东西,编译出来的,虽然是 Native 的可执行文件,但实际上由于设计的原因,lib 库是动态链接,于是你写好的东西,不能像 Go 一样,拷贝一个文件给别人就能跑,还需要安装 libevent, libbdw-gc ... 我看到 GitHub Issue 上有提到过可以编译成静态链接的方式,但似乎这个在 macOS 上不行。

对于不熟悉 C,GCC,LLVM 的人来说,编译是个比较复杂的事情


练习写了两个小东西:

xiaoronglv mark as excellent topic. 13 Apr 12:13

看上去确实没那么美好啊。感觉语法要做的跟 Ruby 一致这点会拖累了它,制约它的发展。但如果不这么做,它还有什么意思呢。想要性能和效率的平衡,还不如用 Elixir 了

Reply to tony612

使用 Java、PHP 才是最好的。

Reply to tony612

其实 Crystal 的作者目标不是完全要做成 Ruby 那样,而是继承 Ruby 优秀的部分,解决不足的部分,例如,上次我问过一个关于 SecureRandom 文件 require 的名字问题。

Ruby 里面是:

require "securerandom"

Crystal 里面是:

require "secure_random"

作者说,这么做是改正 Ruby 的错误,这证明 Crystal 不是在盲目的做成 Ruby 那样,而是在取其精华。


同样,Crystal 也有自己的特点,例如 overload method,而不是 Ruby 那种 options 的动态方式:

def foo(name : String)
end

def foo(name : Integer)
end

Ruby:

def foo(name)
  if name.is_a?(String)
  elsif name.is_a?(Integer)
  end
end

这样做的目的,当然有很大一部分原因是为何性能,减少运行期的不必要开销。

Reply to tony612

至少我目前看到 Crystal 继承 Ruby 的部分已经很不错了,例如:

  • Object 是所有类型的基础,这个可以做很多有意义的事情
  • 各种语法、标准库相同的实现,95% 接近的 API,例如你可以用 Ruby 一摸一样的正则,好处是,实现东西可以很容易参考现有 Ruby 社区的实现,找到对应的方式
  • 不需要像 Elixir 那样放弃以前的思维方式,按函数式编程的方式考虑问题
Reply to tony612

主要还是吸收 ruby 的语法,语言是否优雅还是看个人,不过我觉得 crystal 已经做得足够棒了。

性能方面 benchmarks crystal 一般是跟 java,rust 对打,比 c 慢一点。就是并发不尽人意,等下一个大版本应该会好了。

7 Floor has deleted
Reply to huacnlee

overload method 这个确实不错。

兼容语法这个,虽然长得像,但经你帖子里一说,其实很多语义都不太一样,我反而会觉得更不清不楚了,还不如换个语法来的痛快。

另外,我不觉得“放弃以前的思维方式”是个坏事,学一个新东西,肯定会有各种新的思维方式的冲击,这个也是学习的一个副产品。其实 Crystal 也有很多新的东西吧,不是说长的像,就不需要用新的思维方式来考虑问题了。 (当然,我对 Crystal 完全不懂,有问题的地方请指正)

Reply to saiga

Crystal 吸收的不仅仅是 Ruby 的语法,光说语法太弱了,谁都能模仿,但那些骨子里的风格不是谁都能做的,就如同那些模仿 Rails 的,都只是长得像而已,实际上那些的作者都不懂 Rails 的理念。

在这点上,我认为 Crystal 做到了,看起来几个作者貌似还不是 Ruby 社区很有名的人物(以前都没听说过),但从我了解这段时间来看,Crystal 在继承 Ruby 的思想、理念方面真的很不错,在你实际用的时候能感受到 Ruby 给我带来那些东西。

Reply to huacnlee

“继承 Ruby 的思想、理念”这方面,可以举几个例子吗?

Reply to tony612

这个不太好描述

  • 你在写 Crystal 的时候,你会感觉这是 Ruby,你以前这么做事情你还怎么做
  • 你在写 Go, Elixir 的时候,你会感觉这是 Go, Elixir,你以前的思维方式,实现方式可能都得换掉

这三个我都花了写时间写过一点,当然接触不是太深入,一些感受而已

Go, Elixir 是挺不错的,但问题是,为何我要放弃我认同、并热爱的 Ruby 那套方式?就为了性能?

感觉很屌的样子,不知道这样会不会迎来 Ruby(或者 Crystal) 的一个爆发式增长。

Reply to huacnlee

因为熟悉 Ruby, 基础库强大

Reply to lgn21st

赶紧的!

Reply to lgn21st

期待

估计会有 gem 转换器,自动 转成 crystal 语法兼容格式。

crystal 里面把 or 关键字去掉了,只有 || 了 , 不错

crystal 确实不错,希望能完成生成 WebAssembly。这样用 crystal 来写前端也是很爽的。现在在用 opal,但是性能实在比较差。

先用 ruby boot 了一个编译器,然后在用 crystal 写本身的编译器。

预定个主题大会上说好了

我也觉得语法接近是一个问题,不一致就索性重写,拥抱另一个生态圈。接近又不完全兼容使得重写仍然需要很大的工作量,而现在 crystal 的生态也不够成熟,不看好 Ruby 项目向 Crystal 迁移。

Reply to lgn21st

预约板凳

前十年,大家都在关注如何解决 web 的问题,未来我看更多的是需要解决扩展问题,分布式处理的问题,Elixir 可能更好一些,底子好。

Reply to jimrokliu

Scala 10 年的时候靠着 actor 火了一两年,顺便把 erlang 的热度也带起来了。但是过了 12 年之后,go 和 docker 相继出来就没影了(相对而言)。 scala 的 play 和 erlang 的 chicagoboss 都是相当有特色的 fullstack framework,也是该死的死,该沉寂的沉寂。

老实说,elixir 我觉得能活跃个一两年,之后还是得回归小圈子,自己默默耍着。至于 crystal,看看每月众筹的进度,能不死就烧香拜佛了 🙏

一直觉得原生 erlang 挺好的,所以 elixir 一直没尝试。go 和 crystal 都尝试了,都很喜欢。

Scala 搞的过于复杂了

Reply to daqing

原生的 Erlang 综合起来比 Elixir 要好,尤其是二进制处理的语法。 不管是 Erlang 合适 Elixir 都有一个致命缺点,语言的抽象能力不够。或者说没有 OO 给予类的语言使用方便。 Scala 个人感觉不错。主要是你需要有 FP,OO 的编程背景。

Reply to lilijreey

OO 似乎是个错误,使得你不可能函数编程。

Reply to chenge

scala 就是个例子,证明 OO 和 FP 可以很好一起工作。

Reply to lgn21st

你居然也写 elixir 了?期待感想

我很好奇为什么楼主工作了还有这么多时间研究这些

ruby 程序员时间真的多啊

我最近安装了 Linux, 下面用了 prax.cr 这个替代 pow 的工具,它就是用 crystal 重写的。看看能不能解决我关注的 Ruby 缺少类型检查的 问题。

tomnia in 感觉社区里讨论 Node.js 的话题好少啊 mention this topic. 16 Apr 17:22
Reply to chenge

OO 适合工业,否则为什么是 OO 占据主流这么久呢? 毕竟很多人的水平掌握不了函数式的思维

Reply to nouse

不一定啊,你看看 JS 这几年的变化

Reply to lgn21st

给你和 @huacnlee 预定好今年的大会的主题分享了啊!

Reply to ShiningRay

还有个说法是这是个阴谋。因为企业需要制造些 bug,好收钱。

Elixir 没有 OO,不一样写程序么?

楼主,请问多个文件编译,是怎么处理的?手写 make 么?有没有 ide(>_<)

Crystal 入口文件编译就可以了,例如

b.cr, a.cr

# a.cr
require "./b"

puts "hello world"

运行或编译:

$ crystal build a.cr
Reply to huacnlee

感谢……Good luck

Reply to chenge

OO 又是一个老话题了,编程范式跟着业务抽象来,什么合适用什么。比如 GUI 编程,没有 OO 怎么做?不要拿到锤子就把所有东西当钉子敲。通用编程语言应该支持所有编程范式。

Reply to lilijreey

Erlang 里面,进程就是对象啊,Erlang 里面的 OOP,就是面向进程编程。

Reply to daqing

可以这么理解,我主要说的是类型 OO,而不是概念 OO, erlang 中的类型明显没有 OO 语言的类型功能强大,封装,多态,和继承。recorder,和 map 的语法也不是很简单。

太好了,写着 ruby 风格的代码享受 Go 的性能

Reply to lgn21st

坐等。。。😀

感觉现在它还是有些坑,再观察观察..

今年要推出 1.0,支持 windows .要是有个像 rails 这样的全能框架就好了。

Reply to nil

或者至少有个像 Sinatra 这样的薄框架……

已经有了!Kemal

其实好像确实有几个 Web 框架了,有些还声称和 Rails 接近。选择框架的话可能要参考流行度

56 Floor has deleted

有个比较奇怪的地方是 Crystal 的类型推断,这几乎是这几年最奇怪的类型推断了。举个例子来说,如果这是一段 TypeScript 代码。

function test(param: String | number) {
  if (param instanceof String) {
    return 'Oops'
  }
  return param * 2 // 此处 param 必是一个 number,所以不会有错误抛出
}
test(1) // => 2
test('wat') // => 'Oops'

而 Crystal 中

def test(param : String | Int32)
  if typeof(param) == String
    return "Oops"
  end
  return param * 2.0 # no overload matches 'String#*' with type Float64
                     # Overloads are:
                     #  - String#*(times : Int)
end

puts test(2)
puts test("wat")

无论是把另一个类型放在 return 后还是 else 中都无非规避掉这个问题。也就是说一个方法内某个参数类型的推断并不是基于行的,而是基于方法定义时指定的。这对于一些复杂的情况来说就变得很麻烦,需要大量地依赖重载的方式才能定义完。

Reply to dsh0416

类型判断使用 is_a? 而不是 typeof。 https://play.crystal-lang.org/#/r/244o

Reply to icyleaf

惊了,原来是这样。。。我一开始直接试着写了

param.is_a?String

给我报了 undefined method 'is_a?' for Int32,我还以为这玩意没有 is_a? 方法。。。我才用的 typeof。没想到在 is_a? 后面加个空格就好了。。。这是什么魔法。。。

Reply to dsh0416

关于 typeof 的说明官方文档也有提到:https://crystal-lang.org/docs/syntax_and_semantics/typeof.html

crystal 发起人写的关于 typeof 的一些魔法使用 https://crystal-lang.org/2015/08/24/its-a-typeof-magic.html

Reply to dsh0416

因为 is_a? 是一个方法,在 ruby 和 crystal 正规使用中都可以采用如下两种方式调用:

is_a?(arg)

is_a? arg # <-- 省略括号

Reply to icyleaf

比较奇怪的是,Ruby 中 ? 必须出现在方法名的末尾,所以当你写

3.is_a?Integer #=> true

可以被正确返回,在 Crystal 里当你这么写的时候,我认为 Crystal 也已经把它认为是方法了,因为它报的错误是:

undefined method 'is_a?' for Int32

但我觉得 Crystal 应该是在处理 is_a? 方法上有什么魔法,让这里不工作了。

Reply to dsh0416

实际上这样是不好的习惯,可能是 ruby 单独处理,官方文档的代码范例都是符合规则的 https://ruby-doc.org/core-2.4.1/Object.html#method-i-is_a-3F

比如:

ruby

2.4.0 :010 > "String".is_a?String && false
TypeError: class or module required
        from (irb):10:in `is_a?'
        from (irb):10
        from /Users/wiiseer/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
2.4.0 :011 > "String".is_a? String && false
TypeError: class or module required
        from (irb):11:in `is_a?'
        from (irb):11
        from /Users/wiiseer/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
2.4.0 :012 > "String".is_a?(String) && false
 => false

crystal

icr(0.22.0) > "String".is_a? String && false
 => false
icr(0.22.0) > "String".is_a?(String) && false
 => false

从如上对比可以看出来 crystal 的语法解析才是正确的。

Crystal 要是生态起来了,编译也和 golang 那样方便,那么 golang 感觉没啥搞头了(自我感觉)

这两天也学习了一些 Crystal 的官方文档,说说自己的感受。 首先 Crystal 的作者对这个语言的定位很好,提供的 future 尤其是并发机制是很不错的。类似 Golang. 但是,我们应该注意到一个语言想要发展的好光靠好的卖相是远远不够的,Crystal 现在还处于非常初级的阶段。就编译工具链都没有齐备。调试器,分析器等等。语言最核心的并发机制和 GC 也是非常初级。最总要的是没有一个大公司推动。和 Kotlie 的比较一些。你会发现 Crystal 的前景暗淡。很难在短期内爆红。个人倒觉得 Crystal 如果支持解释执行就厉害了。开发时解释执行,生产环境编译。

上週才看到這篇,本來想說 brew install 裝起來後,build 個 puts "hello word" 確定會動就好,竟然連玩兩個小時沒停擺,坦白說我超喜歡這樣的設計,這幾年拿來當開發主力的 GO 雖然效率不錯,可是寫起來挺囉唆的,Crystal 這 ruby 語法卻又兼有 type, 我認為它在表達性 (expressiveness) 與精確性 (Explicitness) 取得一個很好平衡,更難能可貴的是不用犧牲效率。

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