2016-03-14 10 views
5

In Rails können Sie so etwas wie tun:Gibt es ein Elixir-Äquivalent der "Try" -Funktion von Rails?

@user.try(:contact_info).try(:phone_number)

Welche der phone_number wenn die @user und contact_info beide nicht Null zurückkehren wird. Wenn einer dieser Werte null ist, gibt der Ausdruck null zurück.

Ich möchte wissen, was die meisten idiomatische Weise ist dies in Elixir zu tun, wohl wissend, dass @user und contact_info sind structs.

+2

Ich glaube, Sie nur passende verwenden können Muster, die Situationen zu handhaben, wenn es ist, und es ist nicht 'phone_number' – JustMichael

+0

@JustMichael Sie sollte das eine Antwort geben, da es die beste Lösung ist. –

Antwort

0

Wahrscheinlich nicht, Elixir ist nicht objektorientiert, also rufen Sie bekannte Funktionen auf definierten Modulen auf (wenn die Funktion/das Modul nicht definiert ist, dann kompiliert der Code nicht einmal).

Für sukzessive Aufruf-Funktionen mit dem Rohr-Operator, wo das Ergebnis nil bei jedem Schritt sein kann, denke ich, ein einzelner großer try Block ist ausreichend. Oder Sie können versuchen, die pipe_while Makros aus dieser Bibliothek https://github.com/batate/elixir-pipes

+0

'elixir-pipes' scheint zu tun, was ich will, außer wir müssen ein Muster geben, das zum Erfolg passt. Ich möchte das Gegenteil, etwas, das nicht mit Null übereinstimmt. – Martinos

6

Ich denke, eine Möglichkeit, das zu tun, ist mit Mustervergleich zu gehen, so wäre es so etwas wie diese:

case user do 
    %{contact_info: %{phone_number: phone_number}} when phone_number != nil -> 
    #do something when the phone number is present 
    _ -> 
    #do something when the phone number is absent 
end 
+0

Ob Sie mit einer Case-Anweisung oder einem Funktionskopf eine Mustererkennung durchführen, ist dies wahrscheinlich der Best Way ™. –

-1

Es scheint, dass es keine gebacken werden in Lösung in Elixir. Also habe ich meine eigene zusammengerollt.

defmodule Maybe do 
    def and_then(nil, _), do: nil 
    def and_then(val,fnc), do: fnc.(val) 
    def and_get(struct, key), do: struct |> and_then(&(Map.get(&1, key))) 
end 

no_user = nil 
without_contact_info = %{contact_info: nil} 
with_contact_info = %{contact_info: %{address: "here"}} 

no_user |> Maybe.and_then(&(Map.get(&1, :contact_info))) |> Maybe.and_then(&(Map.get(&1, :address))) 
#> nil 
without_contact_info |> Maybe.and_then(&(Map.get(&1, :contact_info))) |> Maybe.and_then(&(Map.get(&1, :address))) 
#> nil 
with_contact_info |> Maybe.and_then(&(Map.get(&1, :contact_info))) |> Maybe.and_then(&(Map.get(&1, :address))) 
#> "here" 

# or with the less generic `and_get` 

with_contact_info |> Maybe.and_get(:contact_info) |> Maybe.and_get(:address) 
#> "here" 

Es ist wahrscheinlich nicht idiomatische Elixier aber es räumt meinen Code auf.

+0

In diesem speziellen Fall machst du eine Menge defensiver Codierung. Das ist definitiv nicht der idiomatische Weg in Elixir. Der idiomatische Weg in Elixir ist "lass es krachen". Wenn Ihr Benutzer oder contact_info gleich null sind, wie werden Sie danach wiederherstellen? Was ist der andere Weg in diesem Fall? –

+0

Vielleicht etwas wie "N/A". 'with_contact_info |> Maybe.and_get (: contact_info) |> Maybe.and_get (: Adresse) &&" N/A "'. In diesem Fall möchte ich es in einer Ansicht anzeigen, damit es nicht abstürzt, was ich will. – Martinos

+0

Ich glaube, du verstehst falsch, was ich mit "Lass es krachen" gemeint habe. Ich sage nicht, dass die gesamte Anwendung abstürzen wird - nur der Prozess, der versucht, die Informationen zu extrahieren. Grundsätzlich möchten Sie den Prozess starten, um die Daten, die Sie suchen, von einem Supervisor zu extrahieren. Wenn die Daten nicht vorhanden sind, lassen Sie den Prozess abstürzen. Siehe hierzu: http://c2.com/cgi/wiki?LetItCrash für mehr zum Thema. –

1

Sie können get_in von Kernel verwenden:

iex(2)> user = %{contact_info: %{name: "Bob"}} 
%{contact_info: %{name: "Bob"}} 
iex(3)> get_in(user, [:contact_info, :name]) 
"Bob" 
iex(4)> user2 = %{} 
%{} 
iex(5)> get_in(user2, [:contact_info, :name]) 
nil 
iex(6)> user3 = %{contact_info: %{}} 
%{contact_info: %{}} 
iex(7)> get_in(user3, [:contact_info, :name]) 
nil 

Beachten Sie, dass dies nicht mit Ecto-Modelle/structs funktionieren wird, aber schlicht Karten sind in Ordnung.

+0

'get_in' funktioniert nicht mit Structs. In diesem Fall funktioniert das, weil "user" eine Map ist. – Martinos

+0

Danke für die Korrektur, habe meine Antwort – chrismcg

+0

aktualisiert Ich habe gerade die Strukturen fett in meine Frage, so dass es klarer ist. Vielen Dank – Martinos

2

Bearbeiten: Testen meines Vorschlags zu verwenden with, merke ich, es wird nicht funktionieren, wie es kurz auf Match-Fehler, aber Sie haben UndefinedFunctionError auf Null. Also habe ich diesen Teil entfernt. Ich würde mit dem Mustervergleich gehen, wie @JustMichael vorgeschlagen hat.

Wenn Sie die Definition der Strukturen steuern, kann ein Standardwert in der Struktur helfen.

defmodule ContactInfo do 
    defstruct phone_number: nil 
end 

defmodule User do 
    defstruct contact_info: %ContactInfo{} 
end 

iex> user = %User{} 
%User{contact_info: %ContactInfo{phone_number: nil}} 
iex> user.contact_info.phone_number 
nil 

Jemand könnte noch gesetzt absichtlich :contact_info auf Null, aber bedenken Sie nur lassen es krachen ... :)

0
defmodule ContactInfo do 
    defstruct phone_number: nil 
end 

defmodule User do 
    defstruct contact_info: nil 
end 

defmodule Test do 
    def extract_phone_number(%User{contact_info: contact_info}) do 
    case contact_info do 
     %ContactInfo{phone_number: phone_number} -> phone_number 
     _ -> nil 
    end 
    end 
end 

iex(1)> user = %User{} 
%User{contact_info: nil} 
iex(2)> Test.extract_phone_number user 
nil 
iex(3)> user = %User{contact_info: nil} 
%User{contact_info: nil} 
iex(4)> Test.extract_phone_number user 
nil 
iex(5)> user = %User{contact_info: %ContactInfo{phone_number: nil}} 
%User{contact_info: %ContactInfo{phone_number: nil}} 
iex(6)> Test.extract_phone_number user 
nil 
iex(7)> user = %User{contact_info: %ContactInfo{phone_number: 1234567}} 
%User{contact_info: %ContactInfo{phone_number: 1234567}} 
iex(8)> Test.extract_phone_number user 
1234567 

Es ist nicht wie ein Vielleicht Monade funktioniert, aber es ist eine Möglichkeit, Muster zu verwenden Übereinstimmung, um einen Wert aus verschachtelten Strukturen zu extrahieren, während nil behandelt wird. Von dem, was ich bisher gelesen habe (ich bin relativ neu für Elixir selbst), wenn ich Mustervergleiche benutze, scheint dies eher idiomatisches Elixier zu sein.

0

Der nächstgelegene ich so weit kommen kann mit der ok_jose Anwendung ist:

use OkJose 

defmodule Person do 
    defstruct [:name, :address] 
end 

defmodule Address do 
    defstruct [:locale, :state] 
end 

defmodule Locale do 
    defstruct [:number, :street, :city] 
end 

defmodule Main do 
    def main do 
    person = %Person{ 
     name: "Homer", 
     address: %Address{ 
     locale: %Locale{ 
      number: 742, 
      street: "Evergreen Terrace", 
      city: "Springfield", 
     }, 
     state: "???" 
     } 
    } 

    person 
    |> case do %{address: %{locale: %{city: city}}} -> city; _ -> nil end 
    |> String.upcase 
    |> String.reverse 
    |> Pipe.if(&(!is_nil(&1))) 
    |> IO.inspect 
    end 
end 

Main.main 
Verwandte Themen