简体   繁体   English

Elixir 功能在什么时候会成为误导?

[英]At what point does Elixir features becomes a misdirection?

Generally speaking, Functional Programming prides itself for being clearer and concise.一般来说,函数式编程以更清晰和简洁而自豪。 The fact that you don't have side-effects/state management makes it easier for developers to reason about their code and assure behaviours.您没有副作用/状态管理的事实使开发人员更容易推理他们的代码并确保行为。 How far does this truth reach?这个真理能达到多远?

I'm still learning Elixir but given the code from Coding Gnome:我仍在学习 Elixir,但给出了 Coding Gnome 的代码:

def make_move(game = %{ game_state: state }, _guess)
  when state in [:won, :lost] do
    ...
end

def make_move(game = %{ game_state: state }, _guess)
  when state in [:pending] do
    ...
end

def make_move(game, guess) do
  ...
end

One could write it without any fanciness in Javascript as:人们可以在 Javascript 中毫不费力地将其编写为:

const makeMove = (game, guess) => {
  switch(game.state) {
    case 'won':
      return makeMoveFinalState();
    case 'lost':
      return makeMoveFinalState();
    case 'pending':
      return makeMovePending();
  }
}

Disregarding all the type/struct safety provided by Elixir compiler, an Elixir programmer would have to read the whole file before making sure that there wasn't a function with a different signature hijacking another, right?不考虑 Elixir 编译器提供的所有类型/结构安全性,Elixir 程序员必须先阅读整个文件,然后才能确保没有具有不同签名的函数劫持另一个函数,对吗? I feel that this increases the overhead while a programmer, because it's yet another thing you have to think about, even before looking at the implementation of the function.我觉得这会增加程序员的开销,因为这是你必须考虑的另一件事,甚至在查看函数的实现之前。

Besides that, it feels to me as a misdirection because you can't be 100% sure that a case is ending up in that general make_move function unless you know beforehand all others and the signatures types, while with a conditional you have a clearer path of flow.除此之外,在我看来这是一种误导,因为除非您事先知道所有其他函数和签名类型,否则您无法 100% 确定某个案例最终会在该通用make_move函数中结束,而使用条件则您有更清晰的路径的流量。

Could this be rewritten in a better way?这可以用更好的方式重写吗? At what point does these abstractions start to weight in the programmer?这些抽象在什么时候开始对程序员产生影响?

I think this boils down mostly to preference and usually simple exercises with pattern matching with simple conditions do not show the range of "clarity" pattern matching can provide.我认为这主要归结为偏好,并且通常带有简单条件的模式匹配的简单练习不会显示“清晰”模式匹配可以提供的范围。 But I'm suspect because I prefer pattern matching, any way, I'm gonna bite.但我怀疑是因为我更喜欢模式匹配,无论如何,我会咬牙切齿。

In this case, the switch could be said to be more readable and straightforward, but note that there's nothing preventing you from writing a very similar thing in Elixir (or erlang)在这种情况下,可以说 switch 更具可读性和直接性,但请注意,没有什么可以阻止您在 Elixir(或 erlang)中编写非常相似的东西

def make_move(game = %{ game_state: state }, _guess) do
    case state do
       state when state in [:won, :lost] -> # do things
       :pending -> # do things
       _else -> # do other things
    end
end

Regarding the placement of different function clauses for the same function name, elixir will emit a warning if they're not grouped together, so that ends up just being your responsibility to write them together and in the correct order (it will also warn you if any of the branches is by definition unreachable, like placing a catch all before any specific branch that has matchings).关于相同函数名的不同函数子句的放置,如果它们没有组合在一起,elixir 将发出警告,因此最终只是您有责任以正确的顺序将它们组合在一起(它也会警告您,如果根据定义,任何分支都是不可到达的,就像在任何具有匹配项的特定分支之前放置一个 catch all 一样)。

But I think that if for instance you add a slight change of the matching requirements for the pending state, then in my view it starts becoming clearer to write it in the erlang/elixir way.但是我认为,例如,如果您对pending状态的匹配要求稍作更改,那么在我看来,以 erlang/elixir 方式编写它开始变得更加清晰。 Say that when the state is pending there are two different execution paths, depending if it's your turn or something else.假设当状态为pending ,有两种不同的执行路径,这取决于是轮到您还是其他什么。

Now you could write 2 specific branches for that with just function signatures:现在你可以只用函数签名来编写 2 个特定的分支:

def make_move(game = %{ game_state: :pending, your_turn: true }, _guess) do
    # do stuff
end

def make_move(game = %{ game_state: :pending }, _guess) do
    # do stuff
end

To do that in JS you would need to have either another switch, or another if.要在 JS 中做到这一点,你需要有另一个开关,或者另一个 if。 If you have more complex matching patterns then it easily becomes harder to follow, while on elixir I think the paths are quite clear.如果您有更复杂的匹配模式,那么它很容易变得难以遵循,而在长生不老药上,我认为路径非常清晰。

If the other conditions could be more thornier, say when it's :pending and there's nothing on a stack key that holds a list, then again matching that becomes:如果其他条件可能更棘手,请说当它处于:pending并且包含列表的stack键上没有任何内容时,则再次匹配变为:

def make_move(game = %{ game_state: :pending, your_turn: true, stack: [] }, _guess) do

Or if there's another branch where it depends if the first item in the stack was something specific:或者,如果有另一个分支取决于堆栈中的第一项是否特定:

def make_move(game = %{ game_state: :pending, your_turn: true, player_id: your_id, stack: [%AnAlmostTypedStruct{player: your_id} | _] }, _guess) do

Here erlang/elixir would only match this if your_id was the same in both places where it's used in the pattern.如果your_id在模式中使用的两个地方都相同,则 erlang/elixir 只会匹配它。

And also, you say "without fanciness" in JS, but different function heads/arity/pattern matching is nothing fancy in Elixir/Erlang, it's just like the language has support for switch/case based statements at a much lower level (at the module compilation level?).而且,你在 JS 中说“没有幻想”,但是在 Elixir/Erlang 中不同的函数头/arity/模式匹配没什么特别的,就像语言在低得多的级别支持基于 switch/case 的语句一样(在模块编译级别?)。

I for one would love to have effective pattern matching & different function clauses (not destructuring only) in JS.我希望在 JS 中有有效的模式匹配和不同的函数子句(不仅仅是解构)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM