top | item 13564836

Type Driven Domain Modelling, Part 2: Evolving Models with F#

86 points| lucasmreis | 9 years ago |lucasmreis.github.io

65 comments

order

fowlerpower|9 years ago

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.

staticassertion|9 years ago

I would agree if I felt that the industry were moving towards "better" software - as in, as an industry, we said "Wow, we need to seriously take a step back and start writing systems more reliably, more securely, etc".

I do not think we're going in this direction, necessarily.

grumpyprole|9 years ago

Agreed. Objects for everything results in code that is riddled with hidden mutable state and side-effects (state is not encapsulated). It's impossible to reason globally about such systems, even before threads are added. Objects can be used as a reasonable module system, which is why many still advocate them, although IMHO better module systems do exist (e.g. in OCaml).

rubber_duck|9 years ago

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.

shados|9 years ago

Facebook is betting pretty hard on ML via ReasonML (even on the client with BuckleScript), and F# is similar as an ML derivative.

We'll hear about these more and more for sure.

aryehof|9 years ago

Future of our industry? Such languages are popular and suited to certain types of projects and solutions certainly. Particularly for systems in computing, and information and data science.

However, for complex representational systems, including both transactional and continuous systems in business, commerce and industry, other languages and paradigms can remain better suited.

twblalock|9 years ago

> 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.

Mainstream languages will incorporate functional features and remain popular, and they will not be superseded by pure functional languages. Java and C# are already doing this.

cmoscoso|9 years ago

I agree with functional first for backend programs. I don't see functional programming become popular on the front-end thought.

kough|9 years ago

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|9 years ago

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|9 years ago

I call this RSLDD: Red Squiggly Line Driven Development.

lucasmreis|9 years ago

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

kazinator|9 years ago

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|9 years ago

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))))