我最近开始学习Elixir,我真的非常喜欢它,但它是我用过的第一种函数式编程语言。我遇到的问题来自于我一直在阅读教程并在LearnElixir观看截屏视频,你应该尽量避免使用IF
类型语句。
但我发现自己经常嵌套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
答案 0 :(得分:7)
一种方法是使用with
。您可以创建单独的函数,如下所示:
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
然后你可以在你的控制器功能中使用它:
def loginPost(conn, %{"user" => user_params}) do
case authenticate(user_params["email"], user_params["password"]) do
{:ok, user} -> # start session
{:error, error_type} -> # handle error
end
end
这个例子可能会更好,但你明白了。
您也可以阅读此question
的答案答案 1 :(得分:0)
函数式编程的一个好处(和约束)是所有函数必须返回一个值。在类型FP(例如F#,Scala,Haskell)中,函数的所有可能出口的返回类型必须相同。因此,如果输入错误,我不能有一个返回 false 的函数,但如果输入正常,则返回一个数字。如何处理这种情况?
1。)使函数的返回类型成为某种元组。这是许多语言的常见做法。当事情正常时,Erlang有许多函数返回{:ok, value}
,而当问题出现时,{:error, message}
(或类似的东西)会返回:ok
。然后调用函数在使用元组中的第二个元素之前询问原子以确保它是def max(a, b) when is_number(a) and is_number(b) do
。这有点像黑客,但这不是世界上最糟糕的事情。
2。)你可以抛出异常。当然这似乎有点极端,如果它有意义,没有特别好的理由可以避免这种做法。
3。)您可以在输入上添加警戒,以确保您不会在第一时间获得错误的输入。
例如,考虑一下:
max
当然,这可以防止我意外地用一封信或其他任何数字来呼叫with
。人们可以通过额外的警卫进一步限制投入。这将再次消除提前退出的原因之一。
我提供这些作为该问题的其他三种方法。我认为@JustMichael关于使用{{1}}结构的建议也是一个好主意;为了完整起见,我只是添加了这些方法。