Crystal 说我最近关注 Crystal 的感受

huacnlee · 发布于 2017年04月13日 · 最后由 changtimwu 回复于 2017年06月28日 · 6162 次阅读
De6df3
本帖已被设为精华帖!

用 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 的人来说,编译是个比较复杂的事情


练习写了两个小东西:

共收到 63 条回复
1638 xiaoronglv 将本帖设为了精华贴 04月13日 12:13
1232

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

273
1232tony612 回复

使用Java、PHP才是最好的。

De6df3
1232tony612 回复

其实 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

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

De6df3
1232tony612 回复

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

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

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

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

7楼 已删除
1232
De6df3huacnlee 回复

overload method 这个确实不错。

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

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

De6df3
4375saiga 回复

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

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

1232
De6df3huacnlee 回复

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

De6df3
1232tony612 回复

这个不太好描述

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

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

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

96

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

2973
De6df3huacnlee 回复

因为熟悉 Ruby, 基础库强大

De6df3
3lgn21st 回复

赶紧的!

1553
3lgn21st 回复

期待

681

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

681

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

1573
3lgn21st 回复

96

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

9800

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

1107

预定个主题大会上说好了

775

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

1232
3lgn21st 回复

预约板凳

96

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

4375
32jimrokliu 回复

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

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

101

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

101

Scala 搞的过于复杂了

24996
273ruby_sky 回复

呵呵

24996
101daqing 回复

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

4215
24996lilijreey 回复

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

24996
4215chenge 回复

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

2474
3lgn21st 回复

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

24144

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

96

ruby程序员时间真的多啊

C4522b

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

31010 tomnia 感觉社区里讨论 Node.js 的话题好少啊 中提及了此贴 04月16日 17:22
729
4215chenge 回复

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

729
775nouse 回复

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

1107
3lgn21st 回复

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

4215
729ShiningRay 回复

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

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

9435df

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

De6df3
9435dfruby_microbird 回复

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

b.cr, a.cr

# a.cr
require "./b"

puts "hello world"

运行或编译:

$ crystal build a.cr
9435df
De6df3huacnlee 回复

感谢……Good luck

C4522b
4215chenge 回复

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

101
24996lilijreey 回复

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

24996
101daqing 回复

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

611

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

27983
3lgn21st 回复

坐等。。。😀

60a8f6

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

96

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

96
32nil 回复

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

已经有了!Kemal

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

2945

Erlang 的语法继承自逻辑编程语言 Prolog,对大多数程序员来说都太“古怪”或者说晦涩,所以个人觉得 Elixir 的出现的确是一种硬需求,而且现在看 Elixir 发展的很好。

但是 Golang 本身作为一门很新的语言,语法设计非常简洁,比较新的概念可能就是 channel,但是对于熟悉 Linux 编程的用户也很容易理解。其实我想说 Golang 的语法并不晦涩,可能并不需要使用 Ruby 的语法来实现 Golang。

但同时 Golang 作为一门强类型需要编译的语言,如果要实现类似于 Rails 这样的框架就没有 Ruby 来的这么容易,因而我曾经想:是不是可以使用 Rails 作为配置来生成 Go 的代码? 我觉得这可能会是把 Rails 和 Go web 编程结合的比较好的一个方向。

后来我看了一下 Rails generator 的用法,写了一个 go-on-rails, 可以通过在 Rails 里定义好的 model 来生成每个 model 对应的 struct 及进行 CRUD 操作的方法及函数。

这里还有一个简单的 demo

因为本人水平有限,目前还只能支持部分的模型间关联操作、validations 等,代码质量也很一般。如果有兴趣的朋友欢迎提 issue 及 pr。

0b45a6

有个比较奇怪的地方是 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 中都无非规避掉这个问题。也就是说一个方法内某个参数类型的推断并不是基于行的,而是基于方法定义时指定的。这对于一些复杂的情况来说就变得很麻烦,需要大量地依赖重载的方式才能定义完。

9821
0b45a6dsh0416 回复

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

0b45a6
9821icyleaf 回复

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

param.is_a?String

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

9821
0b45a6dsh0416 回复

关于 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

9821
0b45a6dsh0416 回复

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

is_a?(arg)

is_a? arg # <-- 省略括号

0b45a6
9821icyleaf 回复

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

3.is_a?Integer #=> true

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

undefined method 'is_a?' for Int32

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

9821
0b45a6dsh0416 回复

实际上这样是不好的习惯,可能是 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 的语法解析才是正确的。

9592

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

24996

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

96

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

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册