timCF

timCF

Calculus - New data types, private, immutable fields and smart constructors

Hello guys! I implemented another way to create new data types (which are not structs or records) in Elixir. Please check out readme file if you are interested, everything is described in detail with examples:

Thanks!

Most Liked

OvermindDL1

OvermindDL1

Oh hey, this looks awesome!

Somebody can say “hey, this is not a constructor function, it’s a syntactic sugar for value, literal Elixir term”. But anyway, this is expression and value of this expression is Elixir struct of URI type. For simplicity I’ll call this thing as default constructor . And this default constructor is always public. Indeed, you can write in any place or your program something like this:

I so agree, it is, by definition, A Constructor of the type.

This is mostly caused by elixir being deficient in its typing system, which comes from it modeling after erlang instead of trying to fix that bug in erlang.

And then Full Lambda Calculus! Church Encoding! Lol. As I’m reading through I’m curious when church encoding of integers will come up, or the various combinators. ^.^

This looks like a data driven DSEL I saw in Elixir, though function wrapping.

iex> s0.(:pop, nil)
** (RuntimeError) For value of the type Stack got unsupported METHOD=:pop with SECURITY_KEY=nil

Heh, cute, I’m guessing you are using a make_ref() for the security key as they are unique references in the beam world? I’ve not looked at the code yet.

Lambda types are inferior in performance to classical data types like records or structs. I

Yep definitely figured that at the start.

  • λ-type constructors and setters ~ 2 times slower then default constructors and setters for structs
  • λ-type getters ~ 6 - 12 times slower then pattern matching on structs (but this is still pretty nice performance)

Not ‘as’ bad as expected though, that’s within the realm of usability considering structs are raw beam code…

You can run benchmarks with mix bench command in terminal

Ooo, my favorite things! ^.^

At the moment we can’t properly use Elixir protocols with values of λ-types (because of the same reason). I have couple ideas about it and maybe will fix it.

I have a couple of ideas for it to work with my ProtocolEx library though, hmm… If only Tuple Calls weren’t removed from the BEAM! That still bugs me to no end… >.>

Internal state of value of λ-type is vulnerable for reading (not writing!) through Function.info/2 core function. At the moment I don’t know how to fix it:

Eh, there are ways to but it would slow down the implementation more so not sure it is worth it…

It’s possible to read internal state using this function, but it’s still impossible to create new corrupted value of λ-type based on this internal state. So all immutable and private data is still really immutable and all values of λ-type are still valid and safe.

Ehhhh, actually you can… ^.^;
I’m leaving work right now or I’d show you how you can break this. :wink:
Ping me tomorrow if you are curious, but in essence you can create almost any internal beam type with :erlang.binary_to_term, including picking it apart with the inverse.

This is a fun looking project though, no church encoding, just lambda encapsulation though, lol. ^.^

lpil

lpil

Creator of Gleam

This is the same cost of a single atom per type as with structs or records, so probably not a problem :slight_smile:

Eiji

Eiji

I really do not recommend this as BEAM have limit of atoms (1_048_576 by default) and this could cause problems in large scaling.

defmacro __using__(_) do
  quote location: :keep do
    import Calculus, only: [defcalculus: 2]
  end
end

I think it’s bad practice to add extra use if only import is called.

Consider using this:

defmacro calculus(opts) do
  quote bind_quoted: [opts: opts], location: :keep do
    {opts[:state], opts[:return]}
  end
end

instead of:

defmacrop calculus(state: state, return: return) do
  quote location: :keep do
    {unquote(state), unquote(return)}
  end
end

defmacrop calculus(return: return, state: state) do
  quote location: :keep do
    {unquote(state), unquote(return)}
 end
end

Generic code could be imported instead.

Here is my proposition how it could look like:

defmodule Example do
  use Calculus, state: user(id: id, name: name, balance: balance)

  defrecordp user([:id, :name, :balance]), default_return: {:literal, :ok}

  # no need to change state
  calculus get_name([name: name], return: name)
  calculus set_name([], arg: new_name, state: new_name)
end

defmodule Example2 do
  use Calculus, state: sample(sum: sum), default_return: :state

  defrecordp sample([:sum])

  calculus add([sum: sum], arg: integer, when: is_integer(integer), state: sum + integer)
  calculus get([sum: sum], return: sum)
  calculus increment([sum: sum], state: sum + 1)
end

In my example use Calculus should do:

  1. Set @before_compile module attribute
  2. Register accumulate module attribute
  3. Import generic functions
  4. Generate security key

calculus macro accepts 2 arguments:

  1. Bindings (inspired by Ecto.Query API)
  2. Options:
    a) arg: passed extra argument (we could optionally add args option here as well)
    b) return: returns binding or literal
    c) state: changes state
    d) when: defines a guard(s)

Finally @before_compile callback would finalize all defined calculuses

Please let me know what do you think about it.

timCF

timCF

Thanks for review :slight_smile:

No, it is just 64 random bytes converted to atom and inlined to all methods and private expressions in compile-time:

I think pattern matching on inlined atom is the most performant way how I can reach desired behaviour

I’ll check it out :slight_smile:

Oh, this is probably real if you know binary format of erlang terms) And analysis of beam bytecode of the module is real thing as well.
But anyway, this is pretty hard if we compare it with usage of default constructor for pattern mathcing or updating internal data :slight_smile:

Yes, but I got inspiration from Church Encoding - it’s soo good thing. Gives feeling that you have the POWER hahaha

timCF

timCF

But these code examples are not the same things. Your example is less performant because there is call of access protocol in runtime. And it’s also less safe because something like calsulus(state: state, returnnnnn: return) will survive compilation. And the worst thing will happen in runtime - access protocol will return implicit nil for this value, and consequences of this are unpredictable, depends on other code - sometimes it can behave correct, sometimes can behave incorrect, sometimes can raise exceptions.

So my code is just “inline implementation” of named arguments (which not exist in Elixir by default). I did another library for this, but don’t want extra dependency just for one expression:

About other your suggestions about syntax sugar - it’s really sugar, question of preferences in design. Maybe it make sense for someone, maybe not, I don’t know. I thought about DSL with type, private, public, immutable and method keywords - to avoid boilerplate and auto-generate at least getters, maybe some setters. But finally just decided to make interface as much simple and explicit as possible, with smallest possible amount of abstractions. If I, or someone else needs more high-level DSL - he can build it on top of my library pretty easy, because interface is straightforward.

Where Next?

Popular in Libraries Top

Qqwy
TypeCheck: Fast and flexible runtime type-checking for your Elixir projects. Core ideas Type- and function specifications are const...
336 13801 100
New
RobertDober
Earmark is a pure-Elixir Markdown converter. It is intended to be used as a library (just call Earmark.as_html), but can also be used as...
239 11851 134
New
woylie
I released Doggo, a collection of unstyled Phoenix components. Features Unstyled Phoenix components. Storybook that can be added to...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
arkgil
Hi all! I’m happy to announce that Telemetry v0.3.0 is out! This release marks the conversion from Elixir to Erlang so that all the libr...
New
dbern
I’m excited to announce that TaxJar has developed and open-sourced DateTimeParser. We developed it because we found a need to parse user ...
New
josevalim
EDIT: since Ecto 3.0 final version is out, this post was amended to use the final versions in the instructions below. Hi everyone, We a...
New
benlime
I created a new library GitHub - benvp/ex_cva: Class Variance Authority for Elixir which aims to make it very easy to define different va...
New
Qqwy
Solution is a library to help you with working with ok/error-tuples in case and with-expressions by exposing special matching macros, as ...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New

Other popular topics Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New

Sub Categories:

We're in Beta

About us Mission Statement