Hacker News new | past | comments | ask | show | jobs | submit login

While I don’t like Fluent interfaces at all, I don’t like the alternative presented much either.

My dislike of Fluent interfaces stems from two things. First, it’s trying to contort an English-like sentence out of a programming language, which just looks and feels wrong. It leads to weird methods like ‘.method’ in the example. Second, Fluent interfaces really try to paper over the fact that the language lacks an equivalent to Visual Basic’s ‘with’[0] statement.

On the other hand, the example presented is not great either. Should ResponseAssertStatus be an object? Wouldn’t it make more sense to do the request then have a separate line of code to evaluate the status? How does it make sense for BodyOfResponse to be constructed out of some kind of ResponseAssertStatus object?

Between the two options, the Fluent interface makes more sense, even if the underlying implementation ends up being kind of ugly.

[0] https://docs.microsoft.com/en-us/dotnet/visual-basic/languag...




I second the notion that fluency is a language syntax problem that should not be solved with the design of interfaces.

Smalltalk also solved this problem with the semi-colon, but for some damn fool reason we followed C syntax but didn't find an alternative. And now we're trying desperately to replicate `&.` in every library and language, but we still don't have an easy way to write:

    someObject
      .doThis();
      .doThat();
      .dontForgetToFinishUp().


I always wanted a language that was built upon the concept on just passing things forward in a pipe, and was like an hybrid between functional and c style languages, like:

  someobject => doThis() => doThat() => dontForgotToFinishUp()
then you could also possible send multiple objects into that pipe:

  obj1, obj2 => doThis() => doThat() => dontForgotToFinishUp()
you'd have to have some semantics for sending them separately, as an ienumerable or as parameters but perhaps that could be done

  (obj1, obj2) => doThis() => doThat() => dontForgotToFinishUp()

  [obj1, obj2] => doThis() => doThat() => dontForgotToFinishUp()
add on top of that something that could abstract away multithreading/tasking in certain cases (automatically assigning them to tasks) and I'd be a happpy coder :)


I spent a half day playing around with something very similar to this. I wanted a concise language for describing data pipelines in Pandas, and was (ab)using python dunder methods (operator overloading) to this end. Like:

`data | groupby, "author" | mean`

Would create a graph object, which could be lazily evaluated, run in Dask, TF, etc.

It started to get ugly when passing in multiple parameters into a function. I had to watch out for left and right associativity, and manage casting of arguments.

It was a fun little experiment but I'm not sure how much it would actually improve workflows. If that sounds interesting, let me know and I'll poke at it again.


I'm not sure if you are aware, but there are several efforts out there to give Python a more data-pipeline-friendly (composable pipe) syntax:

1) Coconut: http://coconut-lang.org/

2) https://github.com/JulienPalard/Pipe

3) Pandas also has a new dataframe pipe method. https://pandas.pydata.org/pandas-docs/stable/generated/panda...

I would look at those before rolling out a custom solution.


FORTH ?KNOW IF HONK! ELSE FORTH LEARN! THEN

My point is that FORTH is point-free!

https://en.wikipedia.org/wiki/Tacit_programming

Tacit programming, also called point-free style, is a programming paradigm in which function definitions do not identify the arguments (or "points") on which they operate. Instead the definitions merely compose other functions, among which are combinators that manipulate the arguments. Tacit programming is of theoretical interest, because the strict use of composition results in programs that are well adapted for equational reasoning. It is also the natural style of certain programming languages, including APL and its derivatives, and concatenative languages such as Forth. The lack of argument naming gives point-free style a reputation of being unnecessarily obscure, hence the epithet "pointless style."

https://en.wikipedia.org/wiki/Concatenative_programming_lang...

A concatenative programming language is a point-free computer programming language in which all expressions denote functions, and the juxtaposition of expressions denotes function composition. Concatenative programming replaces function application, which is common in other programming styles, with function composition as the default way to build subroutines.


This is actually why I love working in Clojure. There are a number of threading macros to make this pipeline stuff work in different scenarios (put the return value in as the first, last, or variable argument; stop and return nil if you ever get a nil to prevent NPEs from happening downstream; only do this step is a condition is met). A simple example:

    (-> id
        get-account-details ;; {:plan "premium"}
        :plan               ;; "premium"
        get-plan-details    ;; {:price "$100.00"}
        :price)             ;; "$100.00"
It's equivalent to something like this in Python:

    account = get_account_details(id)          # {"plan": "premium"}
    plan_name = account.get("plan")            # "premium"
    plan_details = get_plan_details(plan_name) # {"price": "$100.00"}
    return plan_details.get("price")           # "$100.0"


#!/bin/bash -e -x

(

grep stuff file | wc -l &

expensive_task in/ out/&

)

join

ls out/ | parallel reducers > result


Forth, maybe?


    someObject
    |> doThis
    |> doThat Thing
    |> doThat OtherThing
    |> dontForgetToPayTheBill
or

    let getErDone = 
        doThis 
        >> doThat Thing 
        >> doThat OtherThing 
        >> dontForgetToPayTheBill

    someObject |> getErDone
[not just pseudo-code, that can be compiled with the right definitions]


And that in essence is not really any different than an extension method in C#. At least not from maintainability point of view.


The function chaining is a game-changer, if you're talking about maintainability...

The temporary variables are eliminated,and the type-based chaining self-verifies for new modifications, neither of which you get in C#. Also, thanks to better support for higher-order programming, common post-launch activities like performance upgrades and async tweaking happens with stronger guarantees and oodles less code.

In terms of essence: namespace shielding and modular composition gives numerous opportunities for logical encapsulation and complexity management that are not feasible thanks to C#s extension semantics and extension method requirements. All of the above can be defined in one type, for example, instead of multiple... The end product is just wildly more cohesive in F#/OCaml, with more options throughout to produce more maintainable solutions.

F# can also use (a slightly improved), version of C#s extension semantics, as needed. But the functional composition, currying, Discriminated Unions, and exhaustive pattern matching in conjunction with the module syntax yield a final product that is markedly smaller and distinctly more maintainable than it's C# counterpart.

Not to mention: from a syntax and readability perspective this just destroys its Fluent Interface counterpart, in about a third as many LoC. Start looking into things like custom operator support, DSL support, computation expression support, etc, and the maintenance improving possibilities just blow the .NET baseline out of the water.


I completely agree that F# is superior to C#, I use it when I can, but the gain from "fluent extensions methods" in C# is significant, and improves the code quite a bit. The main benefit over F#, is the pool of developers that understand C# is significantly bigger. This is a parameter I need to take in to consideration because of the developers I hire, there is few and far between the one that grok F# and FP in general.


Fluent extension methods are better than no extensions at all, I agree :)

I tend to look for general competence in new devs, because that persists across platforms and pivots. In general it's odd that we expect devs to know every language, tech, and cloud platform under the sun but draw the line at basic mathematical concepts mapped into programming decades ago... Using both sum and product types is fundamental to proper modelling, IMO.

I also think comparison between C# and F# based on "tricky" FP concepts is really odd in modern times...

On the one hand: if a new hire can't grok FP in F#, how can I trust them to do serious work in C#? OOP & FP synergize wonderfully, and the language has been openly trending towards FP ideals for a decade now. Those FP concepts are now modern idiomatic C# and also very important in tricky domains...

On the other hand, if I'm doing work that makes me think correctness is even a little important why would I want to do it in a language almost-as-good that provides no guarantees and requires more complexity? Why bring a footgun to the party if I'm already worried about quality and some rando new hire? Seems like people who don't grok the basics would do better in a stricter environment with less room for f-ups.

For my money I'll readily take a mild bump in on-boarding costs for productivity, production, and correctness guarantees forever. Not to mention the improved caliber of candidate one gets from finding self-selected A-grade techies and advertising an aggressive dev environment.


In the current situation, getting a competent dev that seems to be able to grasp FP naturally, does not require a mild bump in on-boarding cost, but a significantly higher salary.

Right now I'm hiring developers right out of school and put them through a mentor program, hoping that they turn out good.


Kotlin solves the problem elegantly with the 'apply' and 'run' extension functions.

  fun foo() =
    Runtime.getRuntime().apply {
        addShutdownHook(Thread { println("Bye!") })
        traceMethodCalls(true)
        exit(404)
    }


Which is awfully similar to JavaScript's much maligned 'with' statement.

Never understood the FUD and 'considered harmful' screeds against it. Yes I'm aware of the performance/optimization concerns, but honestly, the whole javascript language is a massive performance/optimization concern and we've managed pretty well.

Simple things like changing style properties on DOM elements:

   with(element.style) {
       display = "block";
       marginLeft = "5px";
       background = "green";
   }
And of course with 'with', everything is a fluid interface, and with the flexibility of being able to enter code between the function calls.


Kotlins `apply` seems exactly like the much maligned `with` statement from JavaScript, which has been deprecated.

Why is it elegant here, but deprecated there?


i suspect some or many of the reasons it's discouraged in js do not apply to kotlin. as we all know, js has... unique constraints in it's design. why is it deprecated in js?


Took the words right out of my mouth (literally, and 5 minutes faster).

Why? I'd say because Douglas Crockford.


With `Arrow`, a Kotlin FP library you can do that

"foo" pipe capitalize pipe toUpperCase pipe reverse


> While I don’t like Fluent interfaces at all, I don’t like the alternative presented much either

If you pull at this sweater thread the end result is working in a language powerful enough to model the 'human readable' bit as structured code, and let mini-DSLs flourish in code bases and interfaces... This may entail leaving OOP partially behind, but it's highly achievable (Scala, F#, Haskell, LISP, etc).


Many times programmers do not even know that their language contains something akin or better than VB's `with` statement. For example, in Ruby `instance_eval` or the even more powerful `instance_exec` could be a drop in replacement for half the stupid fluent interface libraries out there.

The one exception I'll make is query builders like ActiveRecord, as the submitted post mentions. It's hard to figure out how else you can dynamically pass `User.where(age > 18)` to a controller that has the filter to only include men and to paginate by 10 records at a time. I'm not saying it's not possible, it just seems like the one case where the fluent interface is worth the pain it brings. Even so, I really hate them even if I love Rails. Also, scopes are so brittle I avoid them whenever possible. They seem clean and elegant at first, but they're hard to combine and that combining is really only needed as the application gets more and more complicated.


How do you feel about LINQ[0] (specifically, the query expression syntax) as an alternative to the query builders that you mention? From reading this page[1], I'd guess that the query expression syntax compiles down to the same code that the method-based syntax does.

I think it looks great, but I've never had a chance to actually use it. None of the languages that I use regularly have anything even close to LINQ's query expression syntax.

[0] https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...

[1] https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...


Haven't used it but it looks like a syntactic improvement even if some of the underlying issues like difficult debugging may remain persistent. Though really, I shouldn't really answer with a gut opinion. I haven't even toyed around with it.


That feature is, of course, much older than Visual Basic. Pascal had "with" a couple decades earlier.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: