FWIW before I came to Clojure I did a lot of Erlang and in the beginning I 
was at the exact same spot wanting to use pattern matching everywhere 
because it is so damn cool. Same goes for tagged literals.

After a little while I realized that it is just not the way to do it in 
Clojure and forcing the Erlang(or Elixir)-Way onto Clojure is not ideal and 
probably overthinking it. Protocols provide a good/better solution to some 
problems and using Clojure's excellent handling&support of "data" solves 
the rest. While it might feel weird in the beginning, wait a while for 
Clojure to click and you probably won't even miss pattern matching.

Looking at the "(defn register [...])" example. Where is the problem with 
the first solution? It doesn't have the bugs the other implementations have 
and is extremely simple to reason about? The other two solutions do the 
exact same thing just slower with absolutely no gain. If you need the 
"status" abstraction use a real state machine. Don't write a recursive 
function when you don't have to. The code should never be at a point where 
it can be called with forged data and directly skip over the :create and 
:check-unqiue states which is possible in 2 of the 3 solutions.

Embrace the data and data all the things is all I have to say.

Also if you like types (using prismatic/schema [1], core.typed looks pretty 
similar):

(def ContactId s/Int)

(s/defrecord Placeholder
    [text :- s/Str])

(s/defrecord Existing
    [contact-id :- ContactId])

(s/defrecord OneOff
    [name :- s/Str
     email :- s/Str])

(def Recipient
  (s/either PlaceHolder
            Existing
            OneOff))

Just my 2 cents,
/thomas

[1] https://github.com/Prismatic/schema


>  
> Possible F# (might not compipe!!)
>
> Recipient = | Placeholder of string
>                    | Existing of ContactId
>                    | OneoffRecipient of string * string
>
>
> Possible options in Clojure
>
> {:type :placeholder ; or :existing :one-off
>  :placeholder-text :all-my-team-mates
>  :name nil
>  :email nil
>  :contact-id nil
> }
>
> ;; or
>
> (defrecord PlaceholdRecipient [placeholder-text])
> (defrecord ExistingReceipient [contact-id])
> (defrecord OneoffReceipient [name email])
>
> ;; or 
>
> [:one-off name email]
> [:existing contact-id]
> [:placeholder text]
> ;; [:self]
>
> The variant needed not be a two element vector. It is a 1 or more element 
> vector. We could just as easily have a fourth variant [:self] with no data 
> to indicate send message to self. 
>
> To actually use the above data structures and get the actual email 
> addresses from them, we need atleast three methods - 1) fetch the emails of 
> all the users team mates, 2) fetch the email for a contact in the 
> datastore, 3) return the email as is. We could implement these using a 
> multi method or implement some protocol or use a pattern matched method. 
> Each choice has a different extensibility story and are all equally valid. 
> We don't always replace every hashmap with a record. Variants are a viable 
> alternative over using a map like the one shown in the first Clojure 
> choice. 
>
> >> As much as possible I try to build my apps in such a way that the 
> program can self-explore the data you give it to self-optimize, 
> self-extend, or otherwise provide flexibility to the programmer. You can't 
> do that with a variant as a vector. Hand a vector to a function and the 
> logic has to be something like "If this is a two element vector and the 
> first thing is a keyword, then this might be a variant, but I'm not sure 
> because it could also just be a vector". While if you pass in a record, 
> type, or even a variant type, it's trivial to have a program recognize that 
> value and act upon it. 
>
> I have no experience writing apps that had to operate on any arbitrary 
> data with any arbitrary structure. Pretty much all the functions in my apps 
> could rely on data arriving in a particular format in a particular order 
> (positional destructuring). 
>
> More importantly we are pretty much agreeing that [:tagged vectors with 
> some data] are only a representation of a variant. One chosen out of 
> convenience and existing feature set. We could have a defrecord Variant 
> with two fields, a type keyword and value hashmap. If core.match was able 
> to easily pattern match over a record as well as easily destructure the 
> values in the value hashmap, we would use it. From what I have seen of 
> core.match, it is nowhere near as easy/clean as pattern matching a vector.
>
> >> As far as performance goes, this is normally the sort of thing that 
> gets baked into an app at a pretty low level, that's why I suggest it 
> should be as fast as possible. If you're going to be processing millions of 
> these things during the life of an app, and transforming them from one 
> format to another, a little tweaking here and there may save you some pain 
> in the end. 
>
> Pattern matching itself is too slow for it to be viable in any performance 
> sensitive context. That said, I feel optimize first for 
> readability/maintainability, if profiling shows a bottleneck, then and only 
> then optimize that area for performance. 
>
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to