Functional programming 如何在函数式编程语言中使用函数而不是早期返回
我最近开始学习,我真的很喜欢它,但它是我使用过的第一种函数式编程语言。我所面临的问题是,我一直在阅读教程并在上观看屏幕广播,你应该尽量避免使用Functional programming 如何在函数式编程语言中使用函数而不是早期返回,functional-programming,elixir,Functional Programming,Elixir,我最近开始学习,我真的很喜欢它,但它是我使用过的第一种函数式编程语言。我所面临的问题是,我一直在阅读教程并在上观看屏幕广播,你应该尽量避免使用IFtype语句 但我发现自己一直在嵌套cond或case 我会在其他语言(如Golang或Javascript)中解决这些解决方案,只需使用带有提前返回的if语句,这样超出该语句的代码就不会运行,这使我99%的时间不必通过检查假值并返回来嵌套条件 因此,在Elixir(或其他函数式编程语言)中,您如何在不使用嵌套和利用语言功能的情况下,以适当的方式编写以
IF
type语句
但我发现自己一直在嵌套cond
或case
我会在其他语言(如Golang或Javascript)中解决这些解决方案,只需使用带有提前返回的if语句,这样超出该语句的代码就不会运行,这使我99%的时间不必通过检查假值并返回来嵌套条件
因此,在Elixir
(或其他函数式编程语言)中,您如何在不使用嵌套和利用语言功能的情况下,以适当的方式编写以下内容
def loginPost(conn, %{"user" => user_params}) do
# Look for user in database
query = from u in User,
where: u.email == ^user_params["email"],
select: [u.email, u.username, u.password]
data = Repo.all(query)
# Check to see if a user had been found
case (length data) == 0 do
true -> # No user was found, send an error message
conn
|> json(%{ success: false, errors: ["Wrong username or password"]})
false -> # A user was found, compare password
[[email, username, db_password]] = data
case Comeonin.Bcrypt.checkpw(user_params["password"], db_password) do
true -> # Password was correct, set session and return a json response
conn
|> put_session(:authenticated, true)
|> put_session(:username, username)
|> put_session(:email, email)
|> json(%{success: true}) # Send json response and redirect on client side
false -> # Password was incorrect, send an error message
conn
|> json(%{success: false, errors: ["Wrong username or password"]})
end
end
end
end
一种方法是将
与
一起使用。您可以创建单独的函数,如下所示:
def authenticate(email, password) do
with {:ok, user} <- find_user(email),
{:ok, user} <- validate_password(user, password),
{:ok, user} <- validate_preconditions(user)
do: {:ok, user}
end
defp find_user(email) do
# return {:ok, user} if user is found, return {:error, :user_not_found} otherwise
end
defp validate_password(user, password) do
# return {:ok, user} if password is correct, return {:error, :invalid_password} otherwise
end
defp validate_preconditions(user) do
# return {:ok, user} if user is not banned or whatever, return {:error, :cant_be_logged_in} otherwise
end
这个例子可能更好,但你明白了
您也可以从中阅读答案,函数式编程的好处(和限制)之一是所有函数都必须返回一个值。在类型化FP(例如F#、Scala、Haskell)中,函数的所有可能出口的返回类型必须相同。因此,我不能让一个函数在输入错误时返回false,而在输入正确时返回一个数字。如何处理这种情况
1.)使函数的返回类型成为某种元组。这是许多语言中的常见做法。Erlang有很多函数,当事情正常时返回{:ok,value}
,当出现问题时返回{:error,message}
(或类似的东西)。然后,在使用元组中的第二个元素之前,调用函数询问原子以确保它是:ok
。这有点骇人听闻,但这并不是世界上最糟糕的事情
2.)您可以抛出异常。尽管这看起来有点极端,但如果有道理的话,没有特别好的理由避免这种做法
3.)您可以在输入上添加保护,以确保一开始不会收到错误的输入
例如,考虑这一点:
def max(a, b) when is_number(a) and is_number(b) do
当然,这可以防止我不小心用字母或数字以外的任何东西呼叫max
。可以使用额外的防护装置进一步约束输入。这将再次消除提前退出的原因之一
我将这些作为解决这个问题的其他三种方法。我认为@JustMichael关于将
与结构结合使用的建议也是一个好主意;为了完整起见,我添加了这些方法。我更想知道为什么应该避免使用if。。。这根本没有意义。在任何语言中,分支都是程序的组成部分。在一个非平凡的程序中,一定有一些分支发生了。@Hustmphrr我在Elixir的IRC/Slack上与一些人交谈过,他们通常会说,如果我太嵌套了,我想我在一个函数中做的太多了,只会编写另一个函数,这是另外一回事。它适用于所有语言,即避免过多嵌套结构。在fp中,没有循环结构,因此批评者都会指向分支。我明白这一点。更多的是关于模块化。不仅仅是为了避免某些特定的结构。我认为如果使用这个代码会更好。@hustmphrr By code你是指上面给出的例子吗?是的。布尔值上的模式匹配很奇怪。但匹配一组布尔值是一种常见(也是很好)的做法。
def max(a, b) when is_number(a) and is_number(b) do