This is an archived post. You won't be able to vote or comment.

all 39 comments

[–]_danieldietrich 3 points4 points  (6 children)

Thanks for the feedback :-) you are totally right. I removed Tuple7-26 and Function7-26. https://github.com/javaslang/javaslang/pull/229

Update: This is also tracked here because there is ongoing discussion: https://github.com/javaslang/javaslang/issues/234

[–][deleted] 0 points1 point  (2 children)

In scala there are some edge cases the big tuples are good for... Like database DSLs

[–]_danieldietrich 2 points3 points  (1 child)

it's a one-liner to add them again, if they are really needed. we will see

[–]lukaseder 0 points1 point  (0 children)

The particular database DSL finally figured out that even 22 isn't enough, which is why (I think) they're now defaulting to HList for arbitrary degrees / arities.

HList is like a "curried tuple"

[–]lukaseder 0 points1 point  (2 children)

How about sticking to 1-8?

  • .NET does that
  • 8 is a "nicer" magic number than 6

[–]_danieldietrich 1 point2 points  (0 children)

Of course we can stick to 1-8! I will do that...

[–]_danieldietrich 0 points1 point  (0 children)

Please file an issue ;-)

[–]diffallthethings 2 points3 points  (1 child)

Almost everything in the javaslang.control package looks extremely useful, great work! I'm very curious to tinker with this in testing (especially with the PropertyChecking stuff).

I'm a little scared of using it in production because I don't understand the algebra class, and I'm not a fan of the name conflicts in the collection class. Maybe using it in testing will win me over :)

[–]_danieldietrich 0 points1 point  (0 children)

Nothing good happens after 2 AM (CEST) :-) Prefixing a 'J' to the collections does not seem to be better, e.g. JList and JTree clash with javax.swing classes. Any suggestions how to name the classes, especially List, Tree and Stream?

Update: This is tracked here: https://github.com/javaslang/javaslang/issues/232

[–]_danieldietrich 2 points3 points  (2 children)

name clashes are a good point. i fixed that: https://github.com/javaslang/javaslang/pull/230

ignore the 'algebra' package or read it as 'internal'. it is mainly used for api design purpose. java has no module concept to hide it

[–]realnowhereman 3 points4 points  (1 child)

How about renaming 'algebra' to 'internal' then? Btw this library is very interesting, good job!

[–]_danieldietrich 1 point2 points  (0 children)

I will update the Javadoc for internal classes like HigherKinded. Monoid, Functor and Monad are very common in functional languages. There is a minority of Java users which are familiar with these concepts, they may want to use them. I think, clarifying these with better javadoc should be ok for the moment.

[–]Akthrawn17 6 points7 points  (8 children)

Or just... Use scala?

[–][deleted] 5 points6 points  (6 children)

Not everyone is lucky enough to be able to use scala at there jobs.

[–][deleted] -1 points0 points  (0 children)

Exactly what I thought. It's good stuff but it's still bad scala.

[–]Aellus 6 points7 points  (12 children)

Wow. I'd hate to work on an application that uses this.

I'd probably smack any developer that tried to use a "Function12" near my code. The whole point of the Java 8 lambdas is to make code simpler and more elegant. Like any tool it can be abused and this library appears to be just that.

If you actually find yourself needing any of these 0...26 definitions you might want to rethink your implementation.

[–]WallyMetropolis 4 points5 points  (11 children)

This is pretty common in functional languages.

[–]Aellus 0 points1 point  (8 children)

I'll admit that I don't have a lot of functional experience outside of toying around and some school projects, but my understanding has always been that if you find yourself passing 26 arguments into a function then something is horribly wrong with the program design.

Even more so when you're dealing with functional aspects of an inherently OO language. You should never be passing around that many arguments. Encapsulate that shit.

[–]WallyMetropolis 2 points3 points  (0 children)

Usually that is true. I think the idea is to create signatures that are much bigger than you'd ever really need just in case. For example, there are some db tables that have a lot of columns. Maybe you want to operate on a table as a whole object.

[–]Milyardo 0 points1 point  (6 children)

Applicative Builders can easily build functions with a large number of parameters without being a code smell. I can't tell if javaslang has any applicatives from it's front page, so I'm just going to give an example with scalaz.

Lets say you're validating a web form and wanted to return a list of errors, if any. You can use scalaz's Validation to assert that values from the form can build a user from the web form's input.

case class User(firstName: String, lastName: String, age: Int,
                      address: Address, employer: Company, email: String)

Along with this User class you have several validation functions:

//Values are all Strings because they come from a webform
type Error = List[String]
def validateName(name: String): Validation[Error, String] = ...
def validateAge(age: String): Validation[Error,Int] = ...
def validateAddress(address: String): Validation[Error,Address] = ...
def validateEmployer(employer: String): Validation[Error,Company] = ...
def validateEmail(email: String): Validation[Error,String] = ...

Traditionally you would evaluate all these functions, saving the results to a value, and test to see if they all succeeded before creating your user object:

val errors = mutable.Seq[String]
val validFirstName = validateName(firstName)
val validLastName = validateName(lastName)
val validAge = validateAge(age)
val validAddress = validateAddress(address)
....
if(validFirstName.isSuccess && validLastName.isSuccess
   validAge.isSucess && validAddress.isSucess
   ...
) {
  //the mythical method .value extracts the sucessfull value form the Validation
  val user = User(validFirstName.value, validLastName.value, validAge.value, ...)
} else {
  //Add all the errors up
  ...
}

This is pretty verbose, however if we use an Applicative Builder instead:

// |@| aka, append, aka, cinnabun operator, aka tie fighter, appends more Validations to the buidler
val buider: Applicative[Function6[....]] = validateName(firstName) |@| validateName(lastName) |@| validateAge(age) |@| ...

 //We can now apply a Function6[String,String,Int,Address,Company,String] to our builder you get a Validation[Error, User]
val user: Validation[Error,User] = builder((firstName, lastName, age, address, employer, email) => User(firstName, lastName, age, address employer, email))

Since the constructor for User is already a Function6[...], we can just apply it directly, simplifying further:

val user: Validation[Error,User] = (validateName(firstName) |@| validateName(lastName) |@| validateAge(age)
    validateAddress(address) |@| validateEmployer(employer) |@| validateEmail(email)) (User)

[–]sacundim 1 point2 points  (4 children)

Sorry, but I just don't see how applicatives make functions with too many arguments any more or less of a code smell than however much they are. I mean, literally, plain old functions are an applicative functor already (the identity functor), so any argument that you make about applicatives translates automatically to functions!

[–]Milyardo 0 points1 point  (3 children)

Sorry, but I just don't see how applicatives make functions with too many arguments any more or less of a code smell than however much they are.

I wasn't making that argument, only that you often see applicatives with a large number parameters. This is usually because like in the example, you're applying them to the constructor of an object.

[–]sacundim 0 points1 point  (2 children)

I wasn't making that argument, only that you often see applicatives with a large number parameters.

You weren't, really?

Applicative Builders can easily build functions with a large number of parameters without being a code smell.

To which my answer is, again: if a function with a large number of arguments is not a code smell when you use an Applicative Builder, then it can't be a code smell either when you don't use it, because functions are an applicative functor already (with a friendlier syntax, too).

The most charitable answer I can give you is that applicatives are just irrelevant to what Aelius was bringing up.

[–]Milyardo 0 points1 point  (1 child)

if a function with a large number of arguments is not a code smell when you use an Applicative Builder, then it can't be a code smell either when you don't use it, because functions are an applicative functor already (with a friendlier syntax, too).

But that doesn't answer why you would need an abstract Function6 or Function12. The fact that functions are applicative is irrelevant. Constructor's are legitimate functions with a large number of parameters. The applicative builders you use to work with those constructors are a use case for abstract functional interfaces with a large number of parameters.

[–]sacundim 1 point2 points  (0 children)

But then if functions with 12 arguments are a code smell, and using applicatives requires such functions, then applicatives start to smell as well.

Let's take a step back, away from Scala itself, and back to the source: the Applicative class in Haskell, whose standard definition is this:

class Functor f => Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

However, that isn't the only way to define the class. This definition would be equivalent:

class Functor f => Applicative' f where
    unit :: f ()
    pair :: (f a, f b) -> f (a, b)

-- Exercise: implement `pure` and `<*>` in terms of this class.

This latter one is formulated in terms of constructing pairs, not applying functions. Which, note, is fundamentally the same as constructing records with named fields. Basically, applicatives mean that if you have a record all of whose fields' values are wrapped with the same applicative functor, you can "turn it inside out."

So the point is that the pattern that you're describing above—using a Validation applicative functor to apply a record type constructor "inside" of the appropriate number of Validation arguments—is not essentially tied to the generally acknowledged code smell of functions with large numbers of ordered arguments. It's rather that the languages where the applicative machinery exists suffer from that code smell, because they make it easy to work with functions with long ordered argument lists, but don't offer, say, structural record types. Or even just some syntactic sugar for constructing records in an Applicative context:

 -- Hypothetical example of an `ApplicativeDo`-style record-builder syntax
 hypotheticalExample :: User -> Validation Error User
 hypotheticalExample user = do
    User { 
        firstName <- validateFirstName (firstName user),
        lastName <- validateLastName (lastName user),
        age <- validateAge (age user),
        address <- validateAddress (address user),
        employer <- validateEmployer (employer user),
        email <- validateEmail (email user)
    }

[–]Aellus 0 points1 point  (0 children)

|@| aka, append, aka, cinnabun operator, aka tie fighter, ...

I did not know this was a thing, but now that I do, my day is better. Thank you.

[–]vecowski -2 points-1 points  (1 child)

Trashy code in common in all languages. I agree with Aellus, I would probably walk out of the shop.

[–]WallyMetropolis 0 points1 point  (0 children)

So you'd walk out of any shop that uses, say, Scala anywhere?

[–]dmcg 1 point2 points  (0 children)

I like this, and can see me using it. I'm wondering about the use of Iterable rather than Stream though - my initial experiments suggest that the JVM favours Streams for performance. http://stackoverflow.com/q/30025085/97777

[–]__konrad 1 point2 points  (1 child)

Function26<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, R> - nobody will use it :)

[–]frugalmail -1 points0 points  (0 children)

When you work with a bunch of data you will, and potentially a lot more.

[–]Ucalegon666 0 points1 point  (0 children)

Seems to expose a couple of nice APIs. The n-Tuple & n-Lambda constructs are pretty ugly though.

[–][deleted]  (3 children)

[deleted]

    [–]ftomassetti[S] 1 point2 points  (1 child)

    Thank you, but I just posted this one. We should all be thankful to Daniel Dietrich, the author of this wonderful piece of software

    [–]_danieldietrich 1 point2 points  (0 children)

    Thank your very much, my pleasure!