Functional programming 什么时候长生不老药的特性会变成一种误导?

Functional programming 什么时候长生不老药的特性会变成一种误导?,functional-programming,pattern-matching,elixir,Functional Programming,Pattern Matching,Elixir,一般来说,函数式编程以其清晰简洁而自豪。您没有副作用/状态管理这一事实使开发人员更容易对其代码进行推理并确保行为。这一真理能达到什么程度 我仍在学习长生不老药,但根据编写Gnome的代码: def make_move(game = %{ game_state: state }, _guess) when state in [:won, :lost] do ... end def make_move(game = %{ game_state: state }, _guess) w

一般来说,函数式编程以其清晰简洁而自豪。您没有副作用/状态管理这一事实使开发人员更容易对其代码进行推理并确保行为。这一真理能达到什么程度

我仍在学习长生不老药,但根据编写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
人们可以用Javascript编写它,而不需要任何幻想:

const makeMove=(游戏,猜测)=>{
切换(游戏状态){
“赢”一案:
返回makeMoveFinalState();
“丢失”案例:
返回makeMoveFinalState();
“未决”案件:
返回makeMovePending();
}
}

忽略Elixir编译器提供的所有类型/结构安全性,Elixir程序员必须先读取整个文件,然后才能确保没有一个具有不同签名的函数劫持另一个,对吗?我觉得这会增加程序员的开销,因为这是您必须考虑的另一件事,甚至在查看函数的实现之前

除此之外,我觉得这是一种误导,因为你不能100%确定一个案例最终会出现在一般的
make_move
功能中,除非你事先知道所有其他情况和签名类型,而有条件的话,你会有一个更清晰的流程


这可以用更好的方式重写吗?在什么时候,这些抽象开始影响程序员?

我认为这主要归结为偏好,通常简单的模式匹配练习和简单的条件并不能显示模式匹配可以提供的“清晰性”范围。但我很怀疑,因为我更喜欢模式匹配,不管怎样,我会咬的

在这种情况下,可以说切换更具可读性和直观性,但请注意,没有任何东西阻止您使用Elixir(或erlang)编写非常类似的东西

关于为同一个函数名放置不同的函数子句,elixir将在它们没有组合在一起时发出警告,因此,您有责任将它们按正确的顺序写在一起(根据定义,如果任何分支无法访问,它也会向您发出警告,比如在任何具有匹配的特定分支之前放置一个“全面覆盖”)

但我认为,例如,如果您对
挂起
状态的匹配要求添加了一个细微的更改,那么在我看来,以erlang/elixir的方式编写它就变得更加清晰了。假设状态为
挂起
时,有两种不同的执行路径,这取决于轮到您还是其他什么

现在,您可以只使用函数签名为其编写两个特定分支:

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
要在JS中做到这一点,你需要另一个开关,或者另一个if。如果你有更复杂的匹配模式,那么它很容易变得更难遵循,而在elixir上,我认为路径是非常清楚的

如果其他条件可能更棘手,比如当它是
:挂起的
,并且
堆栈上没有保存列表的
键,那么再次匹配会变成:

def make_move(game=%{game_state::pending,您的_回合:true,堆栈:[]},_guess)执行

或者如果有另一个分支,它取决于堆栈中的第一项是否是特定的:

def make_move(game=%{game_state::pending,你的回合:true,player_id:your_id,stack:[%AnAlmostTypedStruct{player:your_id}}|},猜一猜)do

在这里,erlang/elixir只有在模式中使用的两个地方的
您的\u id
相同时才会匹配

而且,你在JS中说“没有幻想”,但是不同的函数头/算术/模式匹配在Elixir/Erlang中并没有什么特别之处,这就像语言在低得多的级别(模块编译级别?)支持基于开关/大小写的语句一样


就我个人而言,我希望在JS中有有效的模式匹配和不同的函数子句(不仅仅是解构)。

我认为这主要归结为偏好,通常简单的模式匹配练习和简单条件不会显示“清晰性”的范围模式匹配可以提供。但我很怀疑,因为我更喜欢模式匹配,无论如何,我会咬

在这种情况下,可以说切换更具可读性和直观性,但请注意,没有任何东西阻止您使用Elixir(或erlang)编写非常类似的东西

关于为同一个函数名放置不同的函数子句,elixir将在它们没有组合在一起时发出警告,因此,您有责任将它们按正确的顺序写在一起(根据定义,如果任何分支无法访问,它也会向您发出警告,比如在任何具有匹配的特定分支之前放置一个“全面覆盖”)

但我认为,例如,如果您对
挂起
状态的匹配要求添加了一个细微的更改,那么在我看来,以erlang/elixir的方式编写它就变得更加清晰了。假设状态为
挂起
时,有两种不同的执行路径,这取决于轮到您还是其他什么

现在,您可以只使用函数签名为其编写两个特定分支:

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
要在JS中做到这一点,你需要另一个开关,或者另一个if。如果你有更复杂的匹配模式,那么它很容易变得更难遵循,而在elixir上,我认为路径是非常清楚的

如果其他条件可能更棘手,比如当它是
:挂起的
,并且
堆栈上没有保存列表的
键,那么再次匹配会变成:

def make_move(game=%{game_state::pending,您的_回合:true,堆栈:[]},_guess)执行

或者如果有另一个分支,它取决于堆栈中的第一项是否是某个东西