我正在尝试学习elixir(但主要是函数式编程)
我正在实现一个非常简单的GenServer,它基本上包含了一个条目列表。每个参数的最大条目数和最大大小(以字节为单位)受到限制(配置文件)
defmodule List do
def init(_) do
{:ok, []}
end
def handle_call({:insert, param1, param2, param3}, from, list) do
import Application
param1_max_size = get_env(:app, ....)
param2_max_size = get_env(:app, ....)
param2_max_size = get_env(:app, ....)
max_items_count = get_env(:app, ....)
## should be {:reply, {:error, :your_list_is_full}, list} if list is full
## should be {:reply, {:error, {:check_this_args_please, wrong_params_list}, list} if any param is wrong. wrong_params_list contains the offending params
## should be {:reply, {:ok}, [{param1, param2, param3} | list ]} otherwise
end
end
我知道这似乎很容易,但基本上我正试图找到一种优雅的功能性方法来做到这一点。我的头是程序性的,我总是在使用=运算符的“嵌套 - 如果 - 其他地狱”结束,如果它是C的那个。
THX
答案 0 :(得分:3)
我知道用于验证多个参数的最简洁方法是Ecto
及其changesets
。您可能希望实现非常相似的东西。
changeset
的方式称呼Ecto
。代码可能是这样的:
changeset = %{data: {param1, param2, param3}, errors: []}
def validate_param(changeset, number) do
max_size = get_env(:app, :"param#{number}"
if elem(changeset.data, number-1) < max_size do
changeset
else
errors = [{:"param#{number}", elem(changeset.data, number-1)} | changeset.errors
%{ changeset | errors: errors}
end
end
validated_changeset =
changeset
|> validate_param(1)
|> validate_param(2)
|> validate_param(3)
case validated_changeset.errors do
[] -> {:reply, :ok, [changeset.data | list]}
errors -> {:reply, errors, list}
end
在Elixir中,您可以使用引号定义原子以包含特殊字符。如果没有特殊字符,则与没有引号相同:
iex(1)> :"a b"
:"a b"
iex(10)> :"ab"
:ab
在内部引号中,您可以使用字符串插值,这样您就可以使用原子validate_param
创建泛型:"param#{number}"
。
elem
函数为零索引,因此我们需要从number
中减去1:elem(changeset.data, number-1)
。
如果没有错误,我们将返回变更集而不做任何修改。
错误将具有结构{field_name, field_value}
。将list
添加到变更集并添加另一个验证函数来检查列表长度并附加正确的错误应该很容易。
您还可以使用validate_param
调用List.foldl
3次删除重复:
List.foldl([1, 2, 3], changeset, fn(number, changeset) -> validate_number(changeset, number) end)
甚至更短:
List.foldl([1, 2, 3], changeset, &validate_number(&2, &1))
最重要的是返回与第一个参数相同类型的函数模式。它可以轻松管道,避免使用许多嵌套的if
或case
语句。