-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create/update filters #2
Comments
The following code is giving Compilation error
Here is my code
Is there a fix for this. I also tried
like it was mentioned in the other issue. I get the following error with this approach |
Validators and filters do not exist yet. This Issue is an idea for a potential future feature, and is not expected to work in the current version of the library.
The If I'm reading your code correctly, I think that maybe you're trying to do something like this (with today's features): defmodule Clock do
def mod(num, denom) do
inter = rem(num, denom)
if inter < 0, do: denom + inter, else: denom
end
defdata do
hour :: non_neg_integer()
minute :: non_neg_integer()
end
def new(hour, minute) do
%Clock{
hour: mod(hour, 24),
minute: mod(minute, 60)
}
end
end |
Yep. That's it. Thanks for reply. 💯 👍 |
A Haskell or PureScript record would be a simple map in Elixir, right? So sidenote: Because all Algae types are structs, if there would be a |
Ehhhh, a haskell record would more closely map to a tuple in Elixir. A row-typed record would map to a map in Elixir. |
Thanks! Yeah, I haven't really gotten to learn about row-types yet, but as you can read above, I just realized that edit: Is there an official forum for the Witchcraft ecosystem? Don't want to mess up the issues with my comments. |
After a couple days, here's may take on #2 and #3, the way Haskell and PureScript smart constructors are working (based on my limited knowledge) where every type is responsible for their own consistency. Some experiments are documented in #37 using two new macros, EDIT:
#2 example
Using defmodule Clock do
import Algae
defdatax do
hour :: Clock.Hour
minute :: Clock.Minute
end
def new(minutes) do
mins = rem(minutes, 60)
hours = div(minutes, 60)
new(hours, minutes)
end
def new(hours, minutes) do
h = Clock.Hour.new(hours)
m = Clock.Minute.new(minutes)
super(h,m)
end
def mod(num, denom) do
inter = rem(num, denom)
if inter < 0 do denom + inter else inter end
end
defmodule Hour do
defdatax do
hour :: integer
end
def new(hour) do
hour
|> Clock.mod(24)
|> super()
end
end
defmodule Minute do
defdatax do
minute :: integer
end
def new(minute) do
minute
|> Clock.mod(60)
|> super()
end
end
end Test: iex(38)> Clock.new(-1, 55)
%Clock{hour: %Clock.Hour{hour: 23}, minute: %Clock.Minute{minute: 55}}
iex(39)> Clock.new(202)
%Clock{hour: %Clock.Hour{hour: 3}, minute: %Clock.Minute{minute: 22}} #3 example
Using defmodule AcuteTriangle do
import Algae
defdatax do
angle1 :: float
angle2 :: float
end
def new(alfa, beta) do
# well, overriding a constructor also overrides type checking...
super(alfa, beta)
case abs(alfa - beta) < 90.0 do
false ->
raise(ArgumentError, "angles are not acute")
true ->
super(alfa, beta)
end
end
end Testing: iex(47)> AcuteTriangle.new(2.0, 127.0)
** (ArgumentError) angles are not acute
iex:54: AcuteTriangle.new/2
iex(47)>
iex(42)> AcuteTriangle.new
#Function<1.38387210/1 in AcuteTriangle.new/0>
iex(43)> AcuteTriangle.new.(2)
** (ArgumentError) not float
(algae) lib/algae/prim.ex:12: Algae.Prim.float/1
iex:43: anonymous fn/2 in AcuteTriangle.new/1
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:43: AcuteTriangle.new/1
iex(43)> AcuteTriangle.new(2.0)
#Function<3.38387210/1 in AcuteTriangle.new/1>
iex(44)> AcuteTriangle.new(2.0).(27.0)
%AcuteTriangle{angle1: 2.0, angle2: 27.0}
iex(45)> AcuteTriangle.new(2.0).(27)
** (ArgumentError) not float
(algae) lib/algae/prim.ex:12: Algae.Prim.float/1
iex:43: anonymous fn/2 in AcuteTriangle.newp/2
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:43: AcuteTriangle.newp/2
iex(45)> AcuteTriangle.new.(2.0).(27.0)
%AcuteTriangle{angle1: 2.0, angle2: 27.0}
iex(46)> AcuteTriangle.new(2.0, 27)
** (ArgumentError) not float
(algae) lib/algae/prim.ex:12: Algae.Prim.float/1
iex:43: anonymous fn/2 in AcuteTriangle.newp/2
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:43: AcuteTriangle.newp/2
iex:50: AcuteTriangle.new/2
iex(46)> AcuteTriangle.new(2.0, 27.0)
%AcuteTriangle{angle1: 2.0, angle2: 27.0} Type checking and constructor override examplesdefmodule Person do
import Algae
defdatax do
name :: string
age :: integer
end
end
defmodule Employee do
import Algae
defdatax do
person :: Person
role :: string
end
def new(person), do: raise(UndefinedFunctionError, "locked")
end Testing iex(7)> Person.new
#Function<1.56597431/1 in Person.new/0>
iex(8)> Person.new("lofa")
#Function<3.56597431/1 in Person.new/1>
iex(9)> Person.new.("lofa")
#Function<3.56597431/1 in Person.new/1>
iex(10)> Person.new(27)
** (ArgumentError) not string
(algae) lib/algae/prim.ex:6: Algae.Prim.string/1
iex:7: anonymous fn/2 in Person.new/1
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:7: Person.new/1
iex(10)> Person.new.(27)
** (ArgumentError) not string
(algae) lib/algae/prim.ex:6: Algae.Prim.string/1
iex:7: anonymous fn/2 in Person.new/1
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:7: Person.new/1
iex(10)> Person.new("lofa").(27)
%Person{age: 27, name: "lofa"}
iex(11)> Person.new("lofa",27)
%Person{age: 27, name: "lofa"}
iex(12)> Person.new("lofa").(:a)
** (ArgumentError) not integer
(algae) lib/algae/prim.ex:3: Algae.Prim.integer/1
iex:7: anonymous fn/2 in Person.newp/2
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:7: Person.newp/2
iex(12)> Person.new("lofa", :a)
** (ArgumentError) not integer
(algae) lib/algae/prim.ex:3: Algae.Prim.integer/1
iex:7: anonymous fn/2 in Person.newp/2
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:7: Person.newp/2 Testing iex(13)> Employee.new
#Function<1.49791001/1 in Employee.new/0>
iex(14)> Employee.new.(27)
** (UndefinedFunctionError) undefined function
iex:14: Employee.new/1
iex(14)> Employee.new(27)
** (UndefinedFunctionError) undefined function
iex:14: Employee.new/1
iex(14)> Employee.new(27, "janitor")
** (FunctionClauseError) no function clause matching in Person.type/1
The following arguments were given to Person.type/1:
# 1
27
iex:7: Person.type/1
iex:9: anonymous fn/2 in Employee.newp/2
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:9: Employee.newp/2
iex(14)> Employee.new(%Person{name: "lofa", age: 27}, "janitor")
%Employee{person: %Person{age: 27, name: "lofa"}, role: "janitor"}
iex(15)> Employee.new(Person.new.("lofa").(27), "janitor")
%Employee{person: %Person{age: 27, name: "lofa"}, role: "janitor"}
iex(16)> Employee.new(Person.new.(2).(27), "janitor")
** (ArgumentError) not string
(algae) lib/algae/prim.ex:6: Algae.Prim.string/1
iex:7: anonymous fn/2 in Person.new/1
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:7: Person.new/1
iex(16)> Overriding type checking for complex typesAn example: defmodule BinaryId do
import Algae
defdatax do
binary_id :: binary
end
def new() do
# Ecto.UUID.generate()
# |> new()
new("binary_id")
end
def type(%__MODULE__{binary_id: "binary_id"}) do
# Ecto.UUID.cast!(binary_id)
end
def type(_), do: raise(ArgumentError, "not #{__MODULE__}")
end
defmodule User do
import Algae
defdatax do
user_id :: BinaryId
name :: string
end
end
iex(4)> BinaryId.new
%BinaryId{binary_id: "binary_id"}
iex(5)> BinaryId.new("lofa")
** (ArgumentError) not Elixir.BinaryId
iex:18: BinaryId.type/1
iex:4: BinaryId.newp/1
iex(12)> User.new(BinaryId.new())
#Function<3.96843422/1 in User.new/1>
iex(13)> User.new(BinaryId.new()).("lofa")
%User{
name: "lofa",
user_id: %BinaryId{binary_id: "87063522-8bd5-42fe-876a-22f3afa12b6c"}
}
iex(16)> User.new("binary")
** (FunctionClauseError) no function clause matching in BinaryId.type/1
The following arguments were given to BinaryId.type/1:
# 1
"binary"
iex:21: BinaryId.type/1
iex:14: anonymous fn/2 in User.new/1
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:14: User.new/1
iex(16)> User.new(<<1,2,3>>)
** (FunctionClauseError) no function clause matching in BinaryId.type/1
The following arguments were given to BinaryId.type/1:
# 1
<<1, 2, 3>>
iex:21: BinaryId.type/1
iex:14: anonymous fn/2 in User.new/1
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
iex:14: User.new/1 |
A Haskell record is here modeled with an Elixir struct |
Haskell's smart constructors are just regular functions without the handy sugar, and generally hiding the normal constructor. Special SyntaxYes, this is the general idea from the issue. I'd be happy to have this functionality if your branch is all good 👍 Overriding Type CheckingHmm, it would be good if the smart constructor didn't need to do this (also if we can avoid subtyping, that would be ideal.) Algae does need some TLC to add type variables and whatnot (the autogenerated types right now are pretty "dumb"), which may help with this issue, if I'm understanding correctly. Hiding the Data ConstructorYou can hide the main struct syntax constructor with a |
I'll clean things up, and will do a pull request for you to review then. Thanks!
You're right, I was totally overthinking this.
I think I'm missing a very basic thing here, because this is new. Would you give an example?
Yes, just realized a couple days ago that even though
Thanks again. As I realized above, I have to stop perceiving Algae as Haskell in Elixir. Algae and others in this family allow more control, but it's still plain Elixir. (I feel stupid reading back the last sentence, but it took me some time to get there...) |
Similar to what can be achieved with a Haskell
newtype
, provide a function to modify or validate an incoming value.Example:
The text was updated successfully, but these errors were encountered: