2016-04-24 8 views
2

私は最近Elixirを学び始めましたが、私は本当にそれを楽しんでいますが、これまでに使ったことのある最初の関数型プログラミング言語です。私が直面している問題は、私がチュートリアルを通して読んでいたことと、LearnElixirでスクリーンキャストを見ていることが、IFタイプのステートメントの使用を避けるべきだということです。早期返品の代わりに関数型プログラミング言語で関数を使用する方法

しかし、私は自分自身常にその先のコードが実行されませんように私は、ただの早期復帰ともしなステートメントを使用してGolangやJavaScriptなどの他の言語でこれらのソリューションを解決するだろうcondまたはcase

をネスト見つけ、これにより、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 
+1

私はなぜ避けなければならないのかについてより興味があります。ただ単に意味をなさないのです。どの言語でも、分岐はプログラムのビルディングブロックです。重要なプログラムのどこかに何らかの分岐が発生している必要があります。 – HuStmpHrrr

+0

@HuStmpHrrr私がElixirのIRC/Slackで話したことのある人は、たいていネストしすぎると、ある機能であまりにも多くのことをやろうとしていて、別のものを書こうと思っているのです。 – Datsik

+0

。すべての言語で適用されます。つまり、ネストされた構造を避けるためです。 fpでは、ループ構造がないので、批評家はすべて分岐を指します。私はポイントを参照してください。それはモジュール性に関するものです。何らかの理由で特定の構造を避けるだけでなく、私は 'if'が使われればこのコードが良くなると思います。 – HuStmpHrrr

答えて

7

行く方法の1つは、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

0

一つの答えを読むことができたことは、すべての機能が値を返さなければならないということです。型付きFP(例えば、F#、Scala、Haskell)では、関数へのすべての可能な終了の戻り型は同じでなければならない。したがって、入力が悪い場合はfalseを返す関数を持つことはできませんが、入力がOKの場合は数値を返します。この状況をどのように処理するのですか?

1.)関数の戻り値の型をある種のタプルにします。これは多くの言語で一般的な方法です。 Erlangには、正常に動作するときは{:ok, value}、問題があるときは{:error, message}(またはそれに近いもの)を返す関数がたくさんあります。次に、呼び出し元の関数は、タプルの2番目の要素を使用する前に、それが:okであることを保証するためにアトムに問い合わせます。これは少しハッキリですが、それは世界で最悪のことではありません。

2.)例外をスローする可能性があります。これはちょっと極端なようですが、それが理にかなっていれば、このプラクティスを避ける特別な理由はありません。

3.)入力にガードを追加して、最初に入力が悪くならないようにすることができます。

たとえば、このことを考慮してください。

def max(a, b) when is_number(a) and is_number(b) do 

これは、もちろん、誤って文字や数字以外本当に何とmaxを呼び出してから私を防ぎます。追加のガードを使用してインプットをさらに制限することができます。これにより、早期終了の理由の1つが取り除かれます。

私はこれらの3つの方法を問題として提供しています。私は@ JustMichaelのwith構成の使用についての提案も素晴らしいアイデアだと思います。私は完全性のためにこれらのアプローチを追加しています。

関連する問題