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

A small request for an idea #4

Open
walkr opened this issue Dec 5, 2016 · 8 comments
Open

A small request for an idea #4

walkr opened this issue Dec 5, 2016 · 8 comments

Comments

@walkr
Copy link

walkr commented Dec 5, 2016

Hello,

I just discovered exceptional and I see you're trying to introduce a few ideas from Haskell to Elixir, which is nice. With that given said, I was wondering if you could give me a few ideas on something I'm trying to accomplish.

I'm currently working with Phoenix and I was thinking about creating a library that will allow me to write my controllers using the following flow:

defmodule AuthController do

    # --- types -----------------------------

    defmodule SMS do
        defstruct code: nil,
    end

    defmodule Flow do
        defstruct conn: nil, params: nil, data: nil
    end

    # --- controller methods -----------------------------

    def authenticate(conn, params) do

        flow = %Flow{conn: conn, params: params, data: %{}}

        flow
        |> initialize(Convertion.params_to_sms_struct)

        # The following steps will apply a series
        # of transformations to flow.data. When a step fails
        # the pipeline will be stopped and the error will be
        # written to conn

        |> success(Data.sanitize)       # Sanitize data
        |> otherwise("bad data")        # Stop, write error to conn

        |> success(Auth.Verify.sms?)    # Verify match in DB
        |> otherwise("invalid code")    # Stop, write error to conn

        # All steps were successful, write final output
        |> finish(:json, fn flow ->
            conn |> json(%{token: flow.data.token})
        end)

    end

end

The main reason I want to do this is because it improves readability, and allows one to think about the flow as a series of linear transformations.

What do you think could be a good approach? My intuition tells me a monad would do the trick, but I must admit, I'm not extremely familiar with monads, monoids and the likes. So I'm still exploring possible solutions, and also learning. (:

Thank you!

@OvermindDL1
Copy link

That is already what conn is for. No need for your Flow struct because everything it encodes is already on conn. The params are already on it as is arbitrary data storage for your own work via the assigns.

@expede
Copy link
Owner

expede commented Dec 6, 2016

First, thanks for writing in, @walkr 😄

I agree with @OvermindDL1. Further, at the controller level, you probably want to actually blow up. Plug will then take the thrown exception, and stick it in an ErrorView of whatever plus_status code is in the exception. You can customize the display of those errors, too.


As a total aside, and for the fun thought experiment: could you use the approach that you mentioned?

My intuition tells me a monad would do the trick, but I must admit, I'm not extremely familiar with monads, monoids and the likes. So I'm still exploring possible solutions, and also learning

Sure, you could use a monad to achieve something like the flow that you're describing above. You might even be able to do some neat stuff with applicatives to test for multiple errors in parallel. I personally would find that linear sequence of success + otherwises a bit confusing ("which scope am I in when this desugars" if they're not directly after each other/ broken up as above), and personally prefer seeing the branches for clarity. I realize that this is essentially the way it's written with JS promises, but I'm personally not a huge fan of that syntax. However, that's totally a matter of preference.

You're essentially trying to wrap things in a specialized Either or Writer (or something along those lines). I happen to have ADT and category-ish algebra libraries for these exact thing. They're both very much WIPs. Witchcraft (the one with monads) in particular is undergoing a major overhaul at the moment, but I've been too busy with a work deadline to wrap it up. Thankfully the holiday break is coming up, so I should have some time, then! 😄 Please feel free to contribute at any of the above if you have time!!

@walkr
Copy link
Author

walkr commented Dec 8, 2016

Thank you @expede and @OvermindDL1 for your replies. They made me aware of some new things.

I was actually reading a cool post about functors, applicative and monads and I really liked one example they gave:

In a procedural language one would do write something along the lines of:

post = Post.find_by_id(1)
if post
    return post.title
else
    return nil

whereas in a functional language, say Haskell, one would employ Maybe and write:

fmap (getPostTitle) (findPost 1)

I personally like the second approach much better.

Anyway, enough with the short rant. I will explore further these interesting concepts and try to apply some to the world of Elixir, where it makes sense.

@OvermindDL1
Copy link

@walkr The stock elixir'ish way of your example would be more like this:

1
|> Post.find_by_id()
|> Post.get_title()

If you handle the nil case internally via pattern matching, but even not doing that it is easy to 'fix that up' via a new call. :-)

@expede
Copy link
Owner

expede commented Dec 8, 2016

Gonna write another wall of text to address a few points that you brought up that have been on my mind. This gets to be the forum for it, I guess 😛 Sorry for the long read!

@walkr yes, absolutely, I hear what you're saying. Haskell is by far my favourite language (though Idris is also pretty exciting), and I'm on a bit of crusade to make it possible to be more Haskell-y in Elixir (see more below).

whereas in a functional language, say Haskell

One thing to note is that while Elixir is a functional language, it's not based around stricter algebras the way the ML family (ML, Haskell, OCaml, Idris) are. You also wouldn't use a Maybe in Lisps (idiomatically). I know that the temptation is to say "Haskell is what a functional language is", but the whole algebras + typeclass hierarchy just one approach. Yes, it's very clean to wrap things in Maybes (or much preferably a custom ADT that expressed the domain), it's providing you with a single interface for flowing through "empty/valueless values" (which sadly places you in an information vacuum for things like serious error handling (where/how did it fail?), for which there are better approaches). This is to say that Maybe "abstracts away the nils cases", but you still need to convert inner nils (or other empty/meaningless value) into Nothings. The end behaviour doesn't have to be that different from @OvermindDL1's suggestion.

That said, the Haskell approach is one that I enjoy greatly! Again, you absolutely can wrap everything in Maybes, but the ecosystem support for that in Elixir is very thin, as it's not idiomatic. Elixir also down't have ADTs. Here's a bootstrapped Maybe if you're interested 😄

I happen to have ADT and category-ish algebra libraries for these exact thing

The libraries that I alluded to in a previous comment (Quark, Algae, Witchcraft) are part of my attempt to write essentially "Elixirz" (like "Scalaz" and "Swiftz"). Elixir doesn't even have the basic combinator out of the box! No id, flip, or compose operator! As such, it's difficult to write in this style in production, and the uninitiated get super confused when you do. Hopefully we can cultivate this style more 💪 but that's very much a public education campaign.

Once the 1.0s of Algae and Witchcraft are wrapped up in a few weeks, I think that it'll make sense to put together a bunch of docs to show people how write in this style. If either of you would like to lend a hand in the new year, I'll definitely be able to use the help! Thoughts?

@OvermindDL1
Copy link

Gonna write another wall of text to address a few points that you brought up that have been on my mind. This gets to be the forum for it, I guess 😛 Sorry for the long read!

Long reads are awesome! Plus if you've seen my posts on the elixirforums I write small novels quite routinely. ^.^

@walkr yes, absolutely, I hear what you're saying. Haskell is by far my favourite language (though Idris is also pretty exciting), and I'm on a bit of crusade to make it possible to be more Haskell-y in Elixir (see more below).

I'm more of an OCaml'er instead of a Haskell'er, I question a few more design decisions in Haskell than I do in OCaml, plus I like 50 source files compiling to be faster code than haskell's in 2 seconds instead of compiling 50 source files in haskell in 5 minutes. ^.^

Once the 1.0s of Algae and Witchcraft are wrapped up in a few weeks

Oh I am so looking forward, I think I use all of your Elixir libraries. ^.^

I think that it'll make sense to put together a bunch of docs to show people how write in this style. If you'd like to lend a hand in the new year, I'll definitely be able to use the help! Thoughts?

My free time is pretty low so unless work is paying for it... But if I can then I will. :-)

@walkr
Copy link
Author

walkr commented Dec 9, 2016

@OvermindDL1 Yes, pattern matching is really great. I think I'm not using it frequently enough. (:

Thanks for laying down your thoughts @expede and injecting some clarity into the conversation.

If either of you would like to lend a hand in the new year, I'll definitely be able to use the help! Thoughts?

I have my hands in too many things right now. I don't think I have enough time to commit to something new. I will definitely keep an eye on it, and contribute if things change on my end.

I think that it'll make sense to put together a bunch of docs to show people how write in this style.

Regarding this, I think it's also important to show people what advantages exists writing code a certain way vs another way; pragmatism not just style.

And thank you both for being part of this conversation. That's really cool!

@OvermindDL1
Copy link

I know that the temptation is to say "Haskell is what a functional language is", but the whole algebras + typeclass hierarchy just one approach.

I'm quite a fan of implicit modules and effect systems of the MC-OCaml world personally. (Implicit) Modules are more powerful than typeclasses (can do everything typeclasses can and more) while being significantly easier/faster to compile. :-)

And the new Effect system coming in OCaml is just amazing. ^.^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants