
Type Driven Domain Modelling, Part 2: Evolving Models with F# - lucasmreis
http://lucasmreis.github.io/blog/type-driven-domain-modelling-part-2/
======
fowlerpower
F# is a beautiful language. You always want to use the right tool for the job
but honestly F# is so right for so many jobs.

I know a lot of people don't want to hear this but these types of languages,
functional first, are the future of our industry. (In the sense that in the
2000s Java like laguanges were the future of our industry). I might be
reaching here, but in my opinion, these are the right languages for the Cloud
and that's why they are getting so popular.

~~~
rubber_duck
I honestly wish this was true but I doubt it. F# is different enough to have a
big curve, and yet the benefits aren't immediately tangible, it's just a bunch
of small things that add up - but try selling that to someone. Meanwhile
mainstream languages are picking up features from it (eg. C# will eventually
have record types and pattern matching). I think languages like this will
influence the mainstream but I don't see them being mainstream in the future.

~~~
vivainio
Huh, the benefits are immediately tangible, the biggest one being less code to
do the same thing while remaining typesafe.

In the meantime, you probably need _slightly_ , but not massively, better
developers.

~~~
jackmott
I don't think you need better developers. I've had one college intern so far
that I've had work with some F# and she liked it better than C#.

Lots of stuff that is easier to do. Not much that is harder.

~~~
rubber_duck
When you don't know a lot learning something different is actually easier than
when you're already proficient in C#. You expect an intern to take x ammount
of time before he can be productive, so if he spends it on learning how to do
it with F# or C# it won't change the x much. But when you have a senior who
knows how to do something with C# he will not want to invest ammount similar
to x, even a 1/2 of the time, because he can get it done with what he knows.

Like I said to OP F# doesn't have that "hard sell", it's just a bunch of
incremental improvements that end up being a big deal together, but each one
on it's own is unimpressive.

~~~
vivainio
You rarely have Tabula Rasa developers - average developer is probably
proficient in Java and/or C#.

I would say HM type inference is a non-incremental "hard sell" improvement,
but it depends on the target audience :).

------
kough
I think there's a typo in the createQty function: shouldn't it return (uint16
0) if n is less than 0, not greater?

Otherwise, great article. This is exactly how I learned to program with
Scheme: mosel the domain carefully, slowly building up helper functions, and
conposing at the end.

~~~
lucasmreis
Thank you! I actually just removed createQty from the code. not only it had a
typo, it was not being used anywhere beside the tests.

And, as some commenter on Disqus said, probably the right thing to do would be
returning an Qty option from the function, and treating it properly.

I have no experience with Scheme, but already worked with Clojure - it was
actually my "gateway" to functional programming languages, hehe!

------
daxfohl
I call this RSLDD: Red Squiggly Line Driven Development.

~~~
lucasmreis
Amazing - I'm gonna use this in the future :)

~~~
daxfohl
It'd be especially nice if database schemas mapped to F# types more readily.
JSON db's _kinda_ do it, but with no defined schema and no joins. It seems
like it'd be not terribly difficult to augment regular SQL databases with
something akin to the F# type system to define table schemas, rather than just
flat data in columns. Then "SQL" could be extended to include `match`
expressions.

~~~
tejasv
I've taken the approach of converting my models to XML, mostly because they
map 1:1 from F#-land to XML-land, and I can use SQL Server's XML Indices, but
most importantly, I can use XSLT to evolve my persisted models upon change
change to F# code.

Then using a bit of F# Quotations, I can convert most match queries to XPath
queries and query SQL Server directly (still work-in-progress).

But running into performance problems, so it's not 100% done.

[http://stackoverflow.com/questions/41949177/f-data-types-
sql...](http://stackoverflow.com/questions/41949177/f-data-types-sql-server-
persistence-using-no-sql-techniques)

~~~
daxfohl
Yeah, I did the first part a couple years ago too. I also put the whole thing
behind a VCS so we'd have version-controlled data. But, yeah, it didn't scale
well enough and I ended up dropping that for mongodb, and later for postgres.

------
kazinator
TXR Lisp:

    
    
      (defstruct product nil
        sku
        price)
      
      (defstruct event nil)
      
      (defstruct add-to-basket event
        product
        quantity)
      
      (defstruct line nil
        product-sku
        quantity
        line-total)
      
      (defstruct basket nil
        lines
        total
        (:postinit (me)
          (set me.total [reduce-left + me.lines 0 (usl line-total)])))
      
      (defvarl empty-basket (new basket))
      
      (defun build-line (product quantity)
        (new line
             product-sku product.sku
             quantity quantity
             line-total (* quantity product.price)))
      
      (defmeth basket add-to (me product quantity)
        (flet ((transform-line (line)
                 (if (equal line.product-sku product.sku)
                   (build-line product (+ line.quantity quantity))
                   line)))
          (let* ((transformed-lines [mapcar transform-line me.lines])
                 (product-already-in-basket (nequal transformed-lines me.lines)))
            (if product-already-in-basket
              (new basket lines transformed-lines)
              (new basket lines (cons (build-line product quantity) me.lines))))))
      
      (defmeth basket update (me event)
        event.(add-to me))
      
      (defmeth add-to-basket add-to (me basket)
        basket.(add-to me.product me.quantity))
    

REPL:

    
    
      $ txr -i typemodel.tl 
      1> (new add-to-basket product (new product sku 42 price 5) quantity 4)
      #S(add-to-basket product #S(product sku 42 price 5) quantity 4)
      2> (new basket)
      #S(basket lines nil total 0)
      3> *1.(add-to *2)
      #S(basket lines (#S(line product-sku 42 quantity 4 line-total 20)) total 20)
    

(The silly implementation of the product-already-in-basket is a literal
transliteration of the original.)

If you see me designing programs like this in real life, just whack me on the
head, please!

~~~
kazinator
Here is a version of basket add-to with a recursive local function for doing
the insert, avoiding the clumsy mapcar and "did we insert or not" check copied
from the F# code.

    
    
      (defmeth basket add-to (me product quantity)
        (labels ((insert (lines)
                   (tree-case lines
                     ((line . rest) (if (equal line.product-sku product.sku)
                                      (cons (build-line product
                                                        (+ line.quantity quantity))
                                            rest)
                                      (cons line (insert rest))))
                     (() (list (build-line product quantity))))))
          (new basket lines (insert me.lines))))

