Skip to content
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

Support for custom types #36

Merged
merged 9 commits into from
Jan 19, 2024
Merged

Support for custom types #36

merged 9 commits into from
Jan 19, 2024

Conversation

solnic
Copy link
Owner

@solnic solnic commented Nov 3, 2023

This adds support for defining custom types

defmodule FilledString do
  use Drops.Type, string(:filled?)
end

defmodule UserContract do
  use Drops.Contract

  schema do
    %{
      required(:name) => FilledString
    }
  end
end

UserContract.conform(%{name: "Jane Doe"})
# {:ok, %{name: "Jane Doe"}}

{:error, errors} = UserContract.conform(%{name: 1})
Enum.map(errors, &to_string/1)
# ["name must be a string"]

{:error, errors} = UserContract.conform(%{name: ""})
Enum.map(errors, &to_string/1)
# ["name must be filled"]

Types are regular structs:

[%{type: type}]= UserContract.schema().keys
# %FilledString{
#   primitive: :string,
#   constraints: {:and, [predicate: {:type?, :string}, predicate: {:filled?, []}]}
# }

@solnic solnic added the feature label Nov 3, 2023
@solnic solnic added this to the v0.2.0 milestone Nov 3, 2023
@Sorc96
Copy link

Sorc96 commented Nov 3, 2023

Hi! As I mentioned on the Elixir forum, I'm a big fan of your work, and I'd like to get some insight into your thoughts when starting a new library like this one.

Specifically, I'm interested in your reasons for making each custom type into its own module instead of defining them as functions like this:

defmodule UserContract do
  use Drops.Contract

  def filled_string do
    Drops.Type.define({:string, [:filled?]})
  end

  schema do
    %{
      required(:name) => filled_string()
    }
  end
end

EDIT: I guess the function syntax could be improved a bit Drops.Type.new(:string, [:filled?])

@solnic
Copy link
Owner Author

solnic commented Nov 3, 2023

@Sorc96 thank you. We need explicit type modules so that it's easier to implement PM-based logic as well as various protocol implementations. If a type was a single struct type, things would get hairy quickly. ie there's already an issue with handling maps because they are too complex, and I clearly see a need for Types.Schema type so that it's handling logic can be better encapsulated.

In general, types are infinitely complex and they must be composable too 😅 I'm modeling things here in a similar way like in dry-types in Ruby, where it works really well. Luckily here, we can just use structs, functions and PM, which is so much simpler and better than complex inheritance hierarchies and various decoration techniques!

Does this makes sense to you?

@Sorc96
Copy link

Sorc96 commented Nov 3, 2023

@solnic Thanks for the explanation!

@solnic solnic marked this pull request as ready for review January 19, 2024 18:46
@solnic solnic merged commit 36139aa into main Jan 19, 2024
4 checks passed
@solnic solnic deleted the 9-support-for-custom-types branch January 19, 2024 18:47
@solnic solnic mentioned this pull request Jan 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants