Hacker News new | ask | show | jobs
by tastyminerals2 2050 days ago
Implicits are not encouraged in our code base and for a good reason.

This was just an example. It's not about filter at all but the general method chaining.

Here is example from D if I must:

    real[] fun(int[] arr) {
        return arr.map!(a => a.to!double / PI).array;   
    }

    void main()
    {
        int[] arr = 100.iota.array; // [0, 1, 2, 3, ...]
        real[] newArr = arr.map!(a=> a*2).array.fun); // [0, 0.63662, 1.27324, ...]
    }
I don't know how you can chain a custom method "fun" to the output of the "map" in Scala without duck typing. Why is this not possible when all conditions type-wise are met? Why you can chain std methods like map, filter, reduce, fold etc. but not custom ones?
2 comments

Ah, I understand what you mean now. You are looking indeed for the thrush operator. I think it should be built into Scala's standard library, but until that happens, you can use the mouse library or build it yourself. Here is an example:

    // Need to define this once somewhere in your project
    implicit class TrushExtension[A](anything: A) {
      def |>[B](function: A => B) = function(anything)
    }
    
    
    // Your application code
    def fun(arr: Iterable[Int]) = arr.map(_.toDouble / Math.PI)
    
    val arr = 0 to 100
    val newArr = arr.map(_*2) |> fun
    
    newArr.foreach(println)  // prints 0, 0.63662, 1.27324, ...
Execute or change the code here: https://scalafiddle.io/sf/WAKhZtJ/0

This is maybe not exactly as convenient, but it comes pretty close.

"someA.someB.someC" becomes "someA |> someB |> someC".

If you would call it pipe operator, like it's called in many (Elm, Elixir, F#, OCaml) other languages that have it, people would understand you faster IMHO. It is even discussed for inclusion into JavaScript.
Nice I didn't know that! I still like to add |> on top of it.

"foo |> bar |> baz" just visually parses easier than "foo pipe bar pipe baz"

Looks good! Thanks for the tip.
Scala is more strict than D for this use case. In Scala, if you want a function to be available to a certain type as if it was a method call, you need to be explicit about it. You have to declare an "implicit class" that takes the base type as an argument, and define the function as a method of the implicit class. You also need to ensure the implicit class is in scope. Once these conditions are met, you can use it as a method.

    val t1 = MyType()
    def fun(t: MyType, argument: Int) = argument

    // can't do this yet
    // t1.fun(42)
    
    // In Scala 2 you use an implicit class to add methods to a type
    implicit class MyEnrichedType(t: MyType) {
      def fun(argument: Int) = fun(t, argument)
    }

    // In Scala 3 you use an extension
    extension (t: MyType)
      def fun(argument: Int) = fun(t, argument)
    
    // now it can be done
    t.fun(42)
Regarding your question about the stdlib methods being able to be chained: they are not special. They are defined for the type the methods return, so they can be used.

Rejecting all kinds of implicits and then complaining about Scala missing features is a bit unfair. "Implicit"is a single keyword, but not a single feature. Implicit arguments, implicit conversions and implicit classes are not the same thing. Fortunately, Scala 3 will clear this misunderstanding.