Hacker News new | past | comments | ask | show | jobs | submit login
Advantages of Functional Programming in Java 8 (bulldogjob.pl)
53 points by Kellanved on Sept 6, 2016 | hide | past | web | favorite | 60 comments



The article isn't about functional programming.

Functional programming is programming with pure (mathematical) functions. As a consequence, functional programming is also programming with (immutable) values (i.e. persistent data-structures, etc).

This isn't pedantry. The whole point of FP is being able to have your code be "referentially transparent", a property that gives you "equational reasoning", making your code more composable, more testable and easier to understand because you can take any function, divorce it from its surrounding context and still make some sense of it. Of course, there's no silver bullet, yada yada, but take those traits away and the definition loses meaning and usefulness almost completely.

And btw I shouldn't need to specify "pure" or "mathematical" if programming wouldn't have overloaded the meaning of the word "function". We could have stuck with "procedure" or "routine" or "sub-routine". As in jumps in code that push and pop the call stack and that have side-effects. And if you want to express the act of programming using side-effectful functions, there's also a perfectly adequate term for it already: "procedural programming".

Yes, it's cool having closures and anonymous functions in your language. It's sort of a prerequisite if you want your FP code to be comfortable. But that's not FP. There's actually nothing resembling FP about the code samples in the article. And the implementation of AccountConverterImpl would make any programmer that likes FP to cringe.

On Java 8: yes, it's becoming possible to do FP, sort of anyway. But it's still painful as hell, with the environment actively working against you. First of all there aren't many libraries that do FP in Java, I only know of functionaljava.org. So you're swimming against the tide, with the standard library itself being actively hostile to FP. And you can start from the fact that Java 8 was released in 2014 and even its new Option type is breaking the Functor laws, because map(x).map(y) != map(x.andThen(y)) and lets not even mention the word monad because it scares people.

But I guess that if you're forced to use Java, well, then you can find some ways to cope with it (especially if you aren't scared of building your own stuff).


My personal experience has been that Java 8 lambda plus deep class hierarchies can lead to some truly impenetrable code (to read that is). In practice mixing oop and the meager functional tools (as you note) in Java can be really, really bad, and makes me weep at the future of Java development. I think teams need to decide what kind of code they're going to write, either 'functional' or oo. And if you are using Java 8 lambda's (and googles immutable collections for example) teams should strive to keep class/interface hierarhies shallow.

I've been doing enterprise java development for 15 years, and the code I'm seeing now is kinda the nail in the coffin for Java for me. I don't want write or maintain that stuff, the next time I switch jobs I'll be looking at a pay cut, and python/django most likely. Or going into management.


The problem with working with closures is that stack-traces tend to become very hard to read, close to unusable sometime. Add asynchrony/concurrency to the mix and you've got a wonderful cocktail :-)

Partial blame is on Sun/Oracle for not adding mutual tail-recursive calls optimizations to the JVM ;-)

This is one of the challenges of FP. It makes debugging harder. But then FP code tends to be tested by means of laws described with QuickCheck, a superb automated testing tool that has ports to Scala, Ocaml, Clojure (that I know of) and it raises the quality of the code a lot.


> As a consequence, functional programming is also programming with (immutable) values

I'll go even further: Values don't exist in time (when did the number 2 suddenly come into existence?), so it doesn't even make sense to ask whether they're “mutable” or “immutable”, which is intrinsically about how objects evolve in time. Values exist in the semantics of the programming language in question (a timeless mathematical object!), and a structural operational semantics makes this very clear: a value is a term that a variable can be substituted with. Ergo:

(0) In a call-by-value language, a value is the result of a successful computation.

(1) In a call-by-name language, a value is a snapshot of a potentially unfinished computation.

(2) In a call-by-need language, a value is the identity of a potentially unfinished computation.

(3) In an object-oriented language, the only values are object identities, and the few lucky primitives that escaped being treated as objects.

> (i.e. persistent data-structures, etc).

You can have ephemeral data structures in a purely functional program too, provided the language has substructural types.


You're right, which is why I named "immutable" in parens, to go along with "mathematical" for functions. Values are just values, but because they are uncommon in mainstream languages, except for primitives and strings maybe, I personally feel the need to mention immutability in order to be precise and not have people confuse them with objects that have identity.


This is a really annoying meme in "pop" FP. Equational reasoning isn't limited to "pure" languages. Effectful functions, as you probably are aware, are just functions of type A -> T B.


I haven't claimed that equational reasoning is limited to pure languages, that's more a property of the code, rather than the language. But you can only achieve equational reasoning with referential transparency and that means the code itself needs to be pure. TFA does not show pure code.

> Effectful functions, as you probably are aware, are just functions of type A -> T B

Not sure how to parse that. But if T in your example is meant to be some IO type, then it is pure along with any transformations on it, until you do unsafePerformIO or something equivalent and you only need to do that once, at the end of the program.


> I haven't claimed that equational reasoning is limited to pure languages

Sorry, I may have read that into your comment.

> the code itself needs to be pure

But that's also incorrect. Just because you can't replace an effectful computation with a value doesn't mean you can't reason equationally. You can also have benign effects that are unobservable, for example, memoization.

> Not sure how to parse that

I meant that functions with effects are just functions with computation type at the meta level. I.e. there's nothing more "mathematical" about only allowing nontermination in your language. It's just the simplest case.


So Lisp is not a functional language...


Technically speaking, Common Lisp isn't a functional language, yes. Common Lisp is a multi paradigm language and you can do FP with it if you want. But from my very limited experience with it, it's not the norm.

However Lisp, the original, is based on the lambda calculus and that is very much about functional programming. So surely you can do FP with most Lisps, but whether that's the norm or not is another question.


So R isn't a functional language...

To be fair the vast majority of people would say that it isn't BUT hidden inside is a very Scheme inspired system that can make pretty functional programs.

http://link.springer.com/chapter/10.1007%2F978-3-642-40447-4...


Scheme isn't a very good functional programming language either. Even cons cells are mutable objects with identity: just use eq? everywhere instead of equal?, and use set-car! and set-cdr! instead of creating new cons cells, and you're back to JavaScript.

---

Sorry, can't reply properly, I'm “submitting too fast”.

<ignore>I understand neither the form nor the content of your objection. Is Scheme a programming language? Yes. What does this have to do with whether it supports functional programming reasonably well?</ignore>

Scheme is a higher-order programming language: procedures are first-class objects whose identities can be bound to variables just like any other value. I'm not sure about calling it “functional”, though: the type of procedures isn't fully abstract, because you can query the physical distinction between two procedures that have the same behavior when called. In other words, in Scheme, procedures are objects, but they are not values.


Scheme is a functional programming language yes?

Sorry but I am an old timer and seeing people blast the paths that led us to where we are is very frustrating to me.

Edit added is a functional


Most things in Lisp have an object identity that can be directly manipulated by programmers, so it is indeed not a functional language.


Technically you are defining a purely functional language and not functional programming.

John McCarthy might disagree with you if he was still with us saying LISP is not a functional language. Here is a great summary of his "Recursive Functions of Symbolic Expressions and Their Computation by Machine" https://swizec.com/blog/the-birth-of-lisp-a-summary-of-john-...

I really think we need a definition of functional programing language. I feel that many just see Haskell and other "pure" functional languages as the only functional programming language when I really don't see that is the point (Also Haskell isn't very pure looking at Monads)

Personal Definition. If you can program in a functional manner and it looks and feel good then it is.

R - More then just this reason but yes JavaScript - Sort of but I would say no if my arm was twisted behind my arm Racket / LISP - Absolutely Python - NO WAY First Class Functions alone don't make a functional language


> Technically you are defining a purely functional language and not functional programming.

No, I'm not defining a purely functional language. ML-family languages (Standard ML, OCaml) aren't purely functional, but they have arbitrary compound values, the physical identity of whose in-memory representation is inaccessible - not a part of the abstraction the programmer is shown. And, FWIW, I only call Haskell “almost purely functional”, because divergence is a kind of effect that its type system doesn't track.

In any case, a function is a mapping from values to values, so a decent treatment of a rich collection of values (including compound values) is a prerequisite for a language to be considered “functional”.

> I really think we need a definition of functional programing language.

Here's a very simple definition: A functional language is a value-oriented language with an abstract type of functions, which can only be distinguished from one another up to extensional equality.

> (Also Haskell isn't very pure looking at Monads)

Monads (resp. monad transformers) can be used to embed the abstract syntax of an effectful language into a pure (resp. less effectful) language. That isn't news to anyone.

> Personal Definition. If you can program in a functional manner and it looks and feel good then it is.

I have no idea what you mean by “program in a functional manner”, but, when I do functional programming, I expect to be able to formally reason about my programs without reaching for Hoare logic, which is an insanely complex beast.


> I really think we need a definition of functional programing language. I feel that many just see Haskell and other "pure" functional languages as the only functional programming language when I really don't see that is the point (Also Haskell isn't very pure looking at Monads)

I consider FP languages to be those that actively encourage an FP style. We live in a pragmatic world with limits, many times we need to work around those limits and working with totally pure code where side effects are modeled by means of monads is sometimes limiting. However, in FP languages the FP style should be considered the default and breaking out of that style should be done for optimization reasons.

The set of features required is not that big. Of course, for static languages, it would be nice if the language had some form of type classes and higher-kinded types, because that goes well with parametric polymorphism, which blends well with FP. But actually, you can't name a certain feature, other than having first class functions maybe. But the community and the available libraries need to encourage a preponderantly FP style.

I can't say exactly what languages are like this, but I can say that Java does not and it never will.


Higher-kinded types aren't an absolute necessity, and, in fact, they force you to pick between modularity (abstract types) and type inference: https://news.ycombinator.com/item?id=12331926 . Not a very pleasant choice, a programmer normally wants both.


Yes, I already said they aren't a necessity, though many people like having higher-kinded types. But then ML/Ocaml folks seem to be doing fine without. I personally prefer having HKT because you can then neatly express well known type-classes for implementing functors, applicatives, monads and many others.


You can recover the power of higher-kinded types with higher-order functors (in the ML sense), without sacrificing the compatibility of abstract types and type inference in the core language.

On the other hand, Haskell's type inference algorithm is simply incompatible with abstract types.


I didn't fully understand your explanation last time. Can you give an example of the conflict between modularity and type inference with HKTs in Haskell?


I can say that surpisingly R (The fastest growing language with a TON of hate for its warts) is the closest I have seen for your description. Though it really is not a general purpose language.

The closest general purpose language I have been exposed to that fits your description is Racket. Based off of Scheme and has the ability to really make your own language from it, but also has pragmatic pieces to get things done.

Love your definition: However, in FP languages the FP style should be considered the default and breaking out of that style should be done for optimization reasons.


Programming with Monads, e.g. the IO Monad, is pure functional programming. It is a common misconception that it isn't. You have full referential transparency and sound composition, since no effects are actually performed until the computation is run/interpreted.


> The article isn't about functional programming.

functional programming =/= functional purity. You're talking about functional purity,not functional programming.


No, I'm talking functional programming: https://en.wikipedia.org/wiki/Functional_programming


fair enough.


I am really wondering why this is on HN front-page. It does not provide any good, in-depth analysis of FP in Java 8. In the contrary, the writing seems to be incoherent and assuming knowledge of a certain Java stack.


Same here this is a very bad blog. I don't see what you can learn from this tutorial. If you want to learn Java I wrote a series https://github.com/shekhargulati/java8-the-missing-tutorial covering new features of Java 8.


Last month I created a badly-written blog post (it started out as an incoherent email written at my day job which I later fleshed out into an incoherent blog post) discussing Java 8's Optional type: https://jamesadam.me/index.php/2016/08/04/thoughts-on-java-8...

In particular, I both love and hate the new streams API. It's nice to work with but easily leads to code that is hard to read and hard to debug.

No, I wouldn't call Java 8 a functional language either. Its 'functional' features are more of a tease than anything else, as you'll quickly discover their limitations (and they definitely feel tacked-on rather than an integral part of the language).


Agreed. A major red flag is the omission of generics usage on their methods. Just using List or Collection without type parameters is just dangerous and very bad advice for a novice programmer seeking enlightenment for Java 8 programming.


It looks like the generics used to be there but have been removed. Because there is one generic type 'T' left in the implementation of ClassUtils.setIfNotNull.

So about this setIfNotNull: I don't understand why this method is used in updateEntity. To me it seems the accountType property in the dto can be set with whatever is in the entity, null or not null. And of course, Java 8 has introduced the Optional class as a better way of dealing with null values, so why bother writing a workaround at all?


I see now, probably just something to do with formatting/escaping, the <> are being swallowed.

Yes, it's strange. I'd prefer to map a null in a DTO to an empty optional. I'd like even more to never have nulls in the dto in the first place.


I assumed the omission was for brevity, but there are annotations and 'final' in a lot of places.

Similarly, methods for mutation (compulsory, no less!) are red flag in FP.


As someone who isn’t familiar with functional programming at all or too familiar with Java, where do I start?


Unless you have a strong reason to use Java, I'd stay far out of it in learning FP. Pick a language where FP model fits well into the language core (e.g., ocaml, Haskell; google for other options), not glommed on as an afterthought to a general purpose language.


Depends on your goals, Java isn't the best if your aim is to learn FP.



For me the biggest advantage of FP languages is exactly not have to deal with `GenericConverter`s, `AbstractDTO`s, `BaseEntity`s and other overly-complex constructs that many OO languages inflict upon the programmer, but to create a small and simple sub-language that solves my problem without making it even more complex.


You can horribly engineer things, regardless if you use haskell or Java.

I've seen Java code like you described, but I've also seen big java projects written in clear, no-nonsense style. For example, look at any open source Java projects released by Google.

I've also seen FP projects that require all engineers to learn category theory. Even when armed with programmers proficient in FP, project did not seem significantly more productive or less buggy from the outside. The approach-ability was lower - you couldn't just quickly glance how something works, even if you knew FP.

The most successful approaches I have seen involved adapting good functional concepts - referential transparency, immutability, compos-ability, minimising local state, functions as objects. They rarely went dogmatically all-in on pure-FP/category-theory/DSL-all-the-things. I strive to write code adhering to those rules, even in non-FP languages, but ultimately favouring practicality and I believe that I get the best of both sides.


When you really think about it, Haskell's mathematically inspired type classes are more or less the equivalent of Java's “`GenericConverter`s, `AbstractDTO`s and `BaseEntity`s”. The main difference is that their meaning is given by algebraic laws, rather than informally specified design patterns or fuzzy human intuitions about how “objects” interact with each other.


Sounds basically like the Kingdom of Nouns argument: http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom...


Every time I see a class like ClassUtils I remember that OO is only good for half of the programs we have to write (1). Problem is, it's a half of each source file. On the other side, OO is a very convenient abstraction for the other half.

(1) I'm sure there are more gracious ways to do that, but maybe not in a single line. That's why we keep seeing classes whose only purpose is to be bags of random methods.


Even C++ has the solution to that - separate namespaces and classes. Allow standalone functions.

    namespace Utils {
         void setIfNotNull(const Supplier& getter, const Consumer& setter) {
             ...
         }
    }

    ....

    using namespace Utils;
    setIfNotNull(...);


I believe you can achieve the same in Java by static import.

EDIT: Also, I think the parent comment was not complaining about the missing possibility of creating standalone functions, but rather saying that not every functionality can be forced into objects, i.e. there is still need of standalone functions in OOP.


You still have to put each method in some class in java, in C++ you can just put it in namespace. It makes more sense, even if in practice the difference is minimal.

> there is still need of standalone functions in OOP

Yes, and that's what I've shown?


It's a syntax issue. Final class with private constructor and all members being static is effectively a namespace.


I think it's more than just syntax - it's culture. In C++, free standing functions has been a part of the culture of the language since the beginning. The design of the language encourages them - there are even cases, such as operator overloading, where it's preferable to implement it as a free standing function rather than a class method. In Java, when I write a class that is a collection of static methods, I feel like I'm fighting against the language and it's culture, even though they are sometimes used.


How much do you write code on Java?

Such functional namespaces were part of Java from the very beginning (Math is a good example and with release of Java 8 now there are many more in standard library). I have not seen an enterprise project yet, in which there's no something ending with Utils or Helper, that is just a collection of functions. Such naming is an anti-pattern, of course, but still, almost every developer in Java world uses this and has it part of it's development culture.


> Such naming is an anti-pattern, of course

This is exactly the cultural problem with too strict adherance to OOP :)

It's like people saying "nails are anti-pattern of course, but people still use them, so we've added hammer-heads to our screwdrivers".


Yes, that was the point I was getting at. It's obviously part of how real Java code is written, but I feel like I'm going against how the language was designed when I do it. (Clearly, everything is not an object, and a class is not always the most appropriate way to group code.)


I'm the OP of this thread.

I add the example of Ruby's modules, that can be included in other modules and eventually classes. Combined with dynamic typing they provide a way to import very generic functions, even if they end up being methods (everything is an object in Ruby.)

Erlang and Elixir are a kind of trade off between functional and OO languages. Formally they are fully functional but their processes (a first class construct) are objects by all means. Processes receive messages (equivalent to method calls) and respond by sending other messages (the caller must pass its process id inside the original message.) We could say that OOP objects are castrated processes because they don't multitask.

What I noticed is that an Erlang/Elixir application has many function calls but far fewer processes/objects than objects in OOP programs. Processes usually encapsulate the main data structures. The equivalent of a Java ArrayList would be a process but its elements don't necessarily need to be processes. Pattern matching and some syntactical sugar help (example: http://culttt.com/2016/04/11/working-keyword-lists-maps-elix...).

Maybe those languages are a more equilibrate middle ground between the extremes of pure OO and pure functional, with the advantages of concurrency and all the external but mandatory infrastructure inside the language (deployment, supervisor trees, etc. - a part of devops.)


Languages that were actually designed to be functional-first, but multi-paradigm, like F# also support this.

Though to be honest, not allowing free functions in Java or C# is kind of silly. It seems like the compiler could pretty easily autogenerate a static class per namespace to actually stick them into, so that it would really just be a bit of syntax sugar.


With using static, or import static, I guess it's not that big a pain point to actually change.


Yeah, I use standalone functions all the time in java. Import static is painless, especially with a good IDE


D have better solution. Modules & packages, allow stand-alone functions.


This is why people don't understand OO. They only see O, not OO.


There's something about these kinds of threads that seems somewhat snobbish and tribalist. There was a similar rant a few weeks ago over whether your 'FRP' is really 'FRP' (https://medium.com/@andrestaltz/why-i-cannot-say-frp-but-i-j...)

I think we can all agree that there's a certain beauty and elegance to completely functionally pure programs. CycleJS for example, really pleases me compared to the complexity of React.

However, like everything in life, the real world is messy and hard to squeeze into binary categories. I can't forsee any large scale program that adheres to such constraints without bending over backwards in weird contortions to meet the limitations imposed, to the extent that such code may even be harder to reason about than mutative code.

I think practical languages 'win' by being 'worse', by compromising and allowing the developer to shoot themselves in the foot a little.


Haskell is absolutely capable of mutation, it's just controlled and tracked. IMHO, immutability is simply a saner default for a high-level language. I think languages "win" due to socio-economic reasons, not technical ones.


> Adding a new Converter for another entity-DTO pair (like User, Address, etc.) needs just creating a new UserConverterImpl class, implementing its own UserConverter, which in turn should implement GenericConverter.

This is exactly what is wrong.

Unnecessary complications resulting from not merely over-engineering but piling up completely unintelligible crap, produced in order to... well, use some Java 8 features.

Imagine that in math instead of doing algebraic simplification one would go into the opposite direction.

Same kind of madness, BTW, is going on in the realm of Javascript frameworks.


FP in Java. HA HA HA OH WOW




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

Search: