Erlang/Elixir 学习 Elixir 有什么新思路么?

chenge · 2016年11月25日 · 最后由 rupertqin 回复于 2016年12月02日 · 8100 次阅读

最近发现谈论这个语言的多起来了,看了一下,似乎确实值得学习。我注意到对 Ruby 的一个改进,y=x,修改 y 不会影响 x,就是所谓的 Immutable,不知道我理解对了没。

原来一般是找本书学,这次打算跟着那个翻译的 Guide 来学,大家有什么新思路么?

今天 Ruby 周刊里看到 changelog.com 是 Elixir 开发的,好像是做播客的,还开源了,有兴趣的不妨看看。

======

点滴心得

学习源码

直接跟着源码学,Elixir 那些模块是用 Elixir 语言写的,是最好的学习范本。 而且源码很清楚,直接跟模块是对应的,在 lib 目录下。

其次再参考

编程练习

推荐下面这个网站,体验很好,难度不大,可参考网友答案,不会停滞不前。

学习资料

推荐

其他参考:

关于 Phoenix:

相关资料:

官方提供的免费午餐

一些学习资料

开源书籍:Elixir 练习曲,etudes-for-elixir

你的理解不对。 不过我也讲不好。y 无法被修改,对 y 进行操作的结果是生成一个新的东西,比如 z。于是会有 x,y,z 这三个东西。

在这里我推荐的书都可以看一下,除了 metaprogramming 那本。

https://ruby-china.org/topics/31662

Elixir 里的等号不是赋值,是 pattern match,programming elixir 里讲等号那章的标题就是

Assignment: I Do Not Think It Means What You Think It Means.”

一句话讲不明白,建议自己看看

Immutable 在 Erlang 里面比较明显

1> X = 1.
1
2> X = 2.
** exception error: no match of right hand side value 2

就是一个“变量”不能被重复赋值,Elixir 变量可以重复赋值,但是底层是做了一点小封装吧。

#3 楼 @piecehealth 我的意思是 x=[1], f(x) 修改 x,不会影响原来的 x。 原来 Ruby 需要 dup,clone 这些麻烦。 是个进步,对吧。

#4 楼 @chenge Erlang 里不可能修改 x 的值 你所谓修改 x 的值,是指修改 x 的 state,函数编程的特点就是没有 state。

比如面向对象编程的话,多次调用 person.talk 方法,结果会根据 person 的 state 改变;函数是编程不存在 state,调用一个方法,参数相同,结果永远相同(除非生成随机数这类的特例)这也是所谓的 no side-effects,总之不要带着用面向对象编程既定的思路来看函数式编程。

别看翻译的

不能说是进步啊。。如果从底层来看,副作用这种东西是最节约内存的,一般 y=x 这种操作会使用 COW 机制,所以一般情况也不会那么的"费内存"。当然你所说的 Immutable 最适合做并发,函数式可以或起来也是因为这个。

#2 楼 @gyorou 为什么说“除了 metaprogramming 那本”?

#8 楼 @theblock24block 凡事都得循序渐进啊。

#10 楼 @etnl 谢谢,这个不错。

@chenge 关于 elixir 变量的问题,我写过一篇 blog 希望对你有帮助。https://blog.lazybee.me/elixir-macro-5/

#12 楼 @falood 谢谢,很深入啊。

14 楼 已删除
15 楼 已删除

#1 楼 @phoenix 我跟着你的例子学了,比较顺利,感觉写得很好。

我想用 Macro 写个 typeof 的函数,但总是报错,这段代码要怎么改。。哎特 all

defmodule Mymacro do
  defmacro warp(type) do
    quote do
      def typeof(x) when unquote(:"is_#{type}")(x), do: unquote(type)
    end
  end
end

defmodule Util do
  import Mymacro
  types = ~w[function nil integer binary bitstring list map float atom tuple pid port reference]
  for type <- types do
    warp(type)
  end
end

#17 楼 @rupertqin 你的代码的目的是什么?没看懂。

#18 楼 @chenge 实现一个对象类型检查的 helper

#19 楼 @rupertqin type 是个字符串,期望输出什么?

错误提示,没看明白啥意思。

** (Protocol.UndefinedError) protocol String.Chars not implemented for {:type, [line: 7], nil}

@chenge 实现这样的功能,只是用宏来写循环了

defmodule Util do
  def typeof(x) when is_function(x), do: "function"
  def typeof(x) when is_nil(x), do: "nil"
  def typeof(x) when is_integer(x), do: "integer"
  ...
end

Util.typeof 999
#=> "integer"

#21 楼 @rupertqin 感觉不用宏更清楚吧。宏我也不懂,等待高明。

#21 楼 @rupertqin 不知道宏该怎么写,但 typeof 这种需求用 Protocols 来做更好吧。 http://elixir-lang.org/getting-started/protocols.html

#23 楼 @darkbaby123Protocols 好像也要一条条地写每一个判断的 function, 就是不想一条条地写所以用一个循环,我这里纠结的地方在于

如果不在循环里写,是可以用 宏创建一个 function 的:

defmodule Mymacro do
  defmacro warp(type) do
    quote do
      def typeof(x) when unquote(:"is_#{type}")(x), do: unquote(type)
    end
  end
end

defmodule Util do
  import Mymacro
  warp("function")  
end

写在循环里就不行,就很纳闷:

  1. 到底有什么不同?
  2. 怎样动态地用宏创建 function?

#24 楼 @rupertqin 我对宏也只是一知半解,但 unquote 还是可用的。一个变通做法如下,来源是这里: http://stackoverflow.com/questions/30498528/how-to-create-a-dynamic-function-name-using-elixir-macro

defmodule Util do
  for type <- ["function", "list", "map"] do
    def typeof(x) when unquote(:"is_#{type}")(x) do
      unquote(type)
    end
  end

  def typeof(_), do: "unknown"
end

IO.puts Util.typeof(fn -> true end)  # function
IO.puts Util.typeof([1, 2, 3])       # list
IO.puts Util.typeof(%{a: 1})         # map
IO.puts Util.typeof(1)               # unknown

关于 DSL 我还是持谨慎态度(并非觉得研究宏没用)。因为我觉得 Elixir 的主张就是 make the complex part explicit 。Jose 在 Domain Specific Language 里拿 validation 举的例子也挺好。

# 1. data structures
import Validator
validate user, name: [length: 1..100],
               email: [matches: ~r/@/]

# 2. functions
import Validator
user
|> validate_length(:name, 1..100)
|> validate_matches(:email, ~r/@/)

# 3. macros + modules
defmodule MyValidator do
  use Validator
  validate_length :name, 1..100
  validate_matches :email, ~r/@/
end

MyValidator.validate(user)

总的来说,数据结构的方式最灵活最容易组合。函数的方式适合复杂的 API,加上管道运算符也足够描述逻辑。宏的方式实现起来最复杂并且限制最大(想想 conditional validation)。这点其实跟 Ruby 社区近几年更推崇传统的 Object 组合而不是更多的 DSL 是一个意思。

#17 楼 @rupertqin #25 楼 @darkbaby123 找到解法了,Dave 书上有,bind_quoted。

defmodule Mymacro do
  defmacro warp(type) do
    quote bind_quoted: [type: type] do
      def typeof(x) when unquote(:"is_#{type}")(x), do: unquote(type)
    end
  end
end
chenge Elixir 会成为明日之星么?似乎是 提及了此话题。 12月06日 10:42
chenge Elixir 会成为明日之星么?似乎是 提及了此话题。 12月06日 10:42
需要 登录 后方可回复, 如果你还没有账号请 注册新账号