News for hackers. Stuff that matters.
Re:From dependency injection to dependency rejection by daxfohl on Sunday February 05, 2017 @03:05AM (#13571431)
Two things:

1. This is overly simplistic. What happens if your IO and logic are by necessity interleaved? Grab X out of DB, grab Y or Z out of DB depending on X's value, etc.? The whole thing just reeks of "ideal case".

2. This is overly complex. All that really needs to be said here is "pull out your pure code when possible". There's nothing special about F# to enable that. The logic in "tryAcceptComposition" is just a function calling other functions; you can do that in C# or even C. The only advantage F# adds here is the piping syntax, which to me only serves to make the code more obtuse. But I guess you couldn't write a three-part series about a single "extract pure function" op.

(This brings up an interesting thought: ReSharper should come up with a way to let you highlight a function and extract the "obviously pure" tidbits automatically).

Re:From dependency injection to dependency rejection by yawaramin on Sunday February 05, 2017 @03:20AM (#13571460)
You may find it interesting to go over a similar conversation I had with the post author a few days ago https://github.com/ploeh/ploeh.github.com/commit/f89c405e92b...
Re:From dependency injection to dependency rejection by daxfohl on Sunday February 05, 2017 @03:39AM (#13571495)
I'll have to learn more about free monads. In general I love F# for doing domain modeling and logic, but I still find OO-style DI better for organizing "services". I've followed ploeh and scott wlaschin for some time and all my attempts to use their DI concepts in my own real-world code have led to code that's less intelligible than IoC with no tangible benefit. It's not for lack of trying, and I think not for lack of intelligence. It just never worked for me.

If free monads could provide something better than standard DI, and (and this is a big caveat) still retain decent editor integration (autocomplete, go-to-declaration/implementation), then I'd check it out. But my gut feeling says that it'll end up being a leaky abstraction that will need undue patching up just to maintain it.

Re:From dependency injection to dependency rejection by willtim on Sunday February 05, 2017 @04:16AM (#13571582)
F# uses .NET classes and objects for a module system, so your use of Objects for "services" is not surprising. An OCaml programmer is much less likely to miss Objects and DI frameworks, as OCaml has a powerful module system (i.e. module functors).

Free Monads can reify an effectful computation, giving flexibility on how it is interpreted. But they are not really a substitute for a good module system.

Re:From dependency injection to dependency rejection by daxfohl on Sunday February 05, 2017 @09:43AM (#13572565)
Okay I think I get free monads now. They seem pretty awesome, essentially letting you plug in an interpreter for the function you're going to run. I can see high-level how this would appear to be a great generic DI option--you set up an "interpreter" to handle the statements in your function however you want: for reals, for test, for reals with logging, etc. And automagically everything gets executed exactly how you want with no additional cruft.

What makes me cautious about the concept though is that e.g. `do_x_and_y()` would be interpreted differently than `do_x(); do_y()`, even if they were fundamentally the same. While "so what?" is a perfectly valid response, that little tidbit just makes me feel like, while FM's are a very cool abstraction for something, it's not really ideal for DI. It's just something meant for a different level. The article "The Wrong Abstraction" comes to mind.

Re:From dependency injection to dependency rejection by yawaramin on Monday February 06, 2017 @12:59AM (#13576888)
> ... you set up an "interpreter" to handle the statements in your function however you want: for reals, for test, for reals with logging, etc.

That is the tip of the iceberg of free monads. Their full power lies in being able to combine different type sof effects into more powerful, composed effects. E.g. you want to do IO while also processing probability distributions using a probability monad. But they can get pretty hairy. See https://youtu.be/qaAKRxO21fU for the gory details.

Re:From dependency injection to dependency rejection by pka on Sunday February 05, 2017 @11:01AM (#13572908)
The monad laws guarantee that there's no difference between do_x(); do_y(); and do_x_and_y(); where the latter is defined as { do_x(); do_y() }. In fact, monads would be pretty useless if that were not the case.
Re:From dependency injection to dependency rejection by daxfohl on Sunday February 05, 2017 @04:50AM (#13571649)
Out of curiosity, OCaml also has traditional classes/interfaces/objects, right? If so, then how do you decide when to use those versus module functors?
Re:From dependency injection to dependency rejection by yawaramin on Sunday February 05, 2017 @12:12PM (#13573295)
Modules and functors are able to contain type definitions, while classes/objects are not. This makes modules practically much more useful for abstraction.
Re:From dependency injection to dependency rejection by willtim on Sunday February 05, 2017 @05:21AM (#13571720)
AFAIK no one uses Objects in OCaml as the module system is sufficiently powerful. The Mirage project is a good example of using OCaml module functors to specialise components.
Re:From dependency injection to dependency rejection by yawaramin on Sunday February 05, 2017 @02:17PM (#13573996)
It's not any lack on your part. F# doesn't really support the really helpful abstraction techniques like parameterised modules or typeclasses. You can roll your own typeclasses using just simple records containing functions. It's easy, idiomatic, and it works statically. E.g., imagine you have a users 'service', with operations 'get by ID', 'add', and 'rename':

    (** Type-safe IDs using a phantom type. *)
    module Id =
      type 'a t = private T of uint64

      let of_uint64 u = T u
      let to_uint64 (T u) = u

    (** A domain type. *)
    module User =
      type t = private { id : t Id.t; name : string; age : int }

      let make uid name age : t = ...
      ...

    (** Users service typeclass. *)
    module User_service =
      type t =
        { get_by_id : User.t Id.t -> User.t Async
          add : User.t -> unit Async
          rename : string -> User.t Id.t -> unit Async }

      let db : t =
        { get_by_id = fun uid -> ...
          add = fun u -> ...
          rename = fun name uid -> ... }

      let test : t =
Now, injecting a user service dependency into any function is equivalent to passing in a parameter of type `User_service.t`.

As for free monads, I don't think F# will make them easy. If you notice, one commenter in that GitHub discussion mentioned they were doing a lot of copy-pasting to implement FMs. Imho that's a bad sign.

Re:From dependency injection to dependency rejection by codedokode on Sunday February 05, 2017 @06:14AM (#13571859)
Having 3 dependencies is pretty typical. The dependencies may have their own dependencies too.

The special thing about F# is a partial function application that is probably missing in C#.

Re:From dependency injection to dependency rejection by daxfohl on Sunday February 05, 2017 @10:41AM (#13572800)
I like the idea of partial application, but in practice I often find it more trouble than it's worth. I find myself spending too much time worrying about "which order to declare function params to allow for greatest utilization of partial application in these different contexts", which ultimately is orthogonal to "write this function, call this function, check in my code", and often makes the code's intention less apparent rather than moreso.

I like it in certain purely functional data crunching routines, where it is idiomatic and can often make the code more generic and more intentional, but I've never had much luck doing DI this way. YMMV.

Re:From dependency injection to dependency rejection by Roboprog on Sunday February 05, 2017 @01:13PM (#13573614)
In case of emergency, break glass :-)

Wrong language, but here is an example of a mechanism to partially apply the trailing arguments, rather than the more customary leading arguments: http://ramdajs.com/docs/#partialRight

And finally, if something in the middle is the thing that needs to be nailed down, you can simply write a one liner function to provide the fixed thing and pass in the rest. (not really PFA any more at that point, but it will get the job done)