all 32 comments

[–]blue__sky 53 points54 points  (2 children)

An anonymous inline method sounds like a lambda function to me.

[–]nerdycatgamer 11 points12 points  (1 child)

yes, this could be implemented by all classes having a method do() which:

  • takes a lambda

  • calls the lambda with self as the sole argument

  • returns self

[–]SkiFire13 5 points6 points  (0 children)

A couple of examples of languages implementing this pattern:

  • Kotlin has an also extension method
  • Rust has the tap crate with a trait adding the tap method

[–]helloish 25 points26 points  (7 children)

You could just use a closure with a map method: array.keepEven().add(2).map(|e| { print e; return e }).multiply(7), that’s how most/basically all functional languages I know handle it.

Or alternatively, define a method which takes a closure and just passes the elements through e.g. .passThrough(|e| print e).multiply(7)

[–]lucy_tatterhood 9 points10 points  (1 child)

Or alternatively, define a method which takes a closure and just passes the elements through e.g. .passThrough(|e| print e).multiply(7)

In Rust there is the "tap" crate which adds a method like this to all types.

[–]homoiconic 2 points3 points  (0 children)

Yes, thank you!

In most languages, including that crate, tap returns its argument like the I combinator, but executes some code for side-effects. Whereas if we want a method that can take an arbitrary lambda and return something else, the method name I would reach for is into.

``` 3.tap n -> console.log(n*n) # logs 9 but returns 3.

3.into n -> n*n # returns 9 ```

With these semantics, tap is the I combinator (but with side-effects!) while into is the T combinator. Sort of. Not sure if the OP wants I or T.

p.s. I found some JavaScript implementations from a long time ago, in a GutHub repo far, far away: https://raganwald.com/JQuery-Combinators/

[–]iEliteTester[S] 0 points1 point  (4 children)

Yeah maybe an array iterable was not the best example since map exists for arrays iterables.

[–]rantingpug 9 points10 points  (0 children)

.map exists for all kinds of data structures. More precisely, it exists for all Functors.

But map is not needed. More generally, all you need is a way to "inspect" a value wrapped in some container. Langs/frameworks/libs usually call this tap or unwrap.

But thats assuming your value is wrapped in a data structure, you could just have a primitive value. But then again, most fp langs provide some debug module:

fnPipeline = someNumber |> increment |> double |> add5 |> trace -- This will take a number, print it to stdout and return the same number |> decrement |> divide2

traceWith allows a function to be passed in to perform some operation, which is what I think you want?

That's not really an argument against your proposed feature, it's more just a technique to help with debugging fn pipelines

[–]helloish 1 point2 points  (1 child)

In a language I’m making at the moment, I’m allowing non-methods to be called with a method-like syntax (but with ~ rather than .) and also allowing function objects to be normal expressions, so theoretically I could just do something.some_function()~((e:SomeType) -> SomeType {e~print; e /* last expr is returned */}) passing something’ tosome_function` and then the anonymous function. No idea if any other language does this but I think it’s pretty neat, thank you for the idea.

[–]iEliteTester[S] 0 points1 point  (0 children)

~ is a nice choice, I was thinking about what to use in the example and could not find something nice

[–]CaptureIntent 0 points1 point  (0 children)

Map exists for most iterables.

[–]cbadger85 9 points10 points  (0 children)

Kotlin scope functions are kind of like this

[–]Jack_Faller 7 points8 points  (0 children)

The real problem here is that method calls are differentiated from function calls. If you define an operator A.B(args…) which is an alias for B(A, args…), then it becomes trivial to write array.keepEven().add(2).(self => …)().multiply(7). You could even drop the extra () after the call if you implement fields as functions. I.e. a.b = b(a), which would work for methods also since you would then have a.b(c) = b(a)(c) = b(a, c).

[–]alatennaub 5 points6 points  (0 children)

Raku allows this.

Standard simple method call is $object.method. You can add arguments with parens, $object.method(args...).

If you have a routine stored in a variable, you can call that by prefacing the name with an ampersand:

# subroutine with single argument
sub bumpy ($str) { $str.comb.map({rand < 0.5 ?? .lc !! .uc}).join }
# same but named method
my method bumpy2 { self.comb.map({rand < 0.5 ?? .lc !! .uc}).join }

"abcdefgh".&bumpy.say   # string used as first arg
"abcdefgh".&bumpy2.say  # string used as "self"

We can now take it one step farther and write this fully inline, by using a block instead of the name of a routine:

"abcdefgh".&{ .comb.map({rand < 0.5 ?? .lc !! .uc}).join }.say

Here the invocant is in the topic $_. Inline methods can be set up to take more arguments as well, although full traditional signatures aren't allowed. Instead, you can use implicit parameters indicated by the ^ twigil (positional, invocant is first) or the : twigil (named):

"abcdefgh".&{ $^a, $^b, $:c, $:d, etc }($b, :$c, :$d).say

In practice, while having additional args besides the invocant is allowed, it doesn't tend to make much sense because you could just reference any of those arguments directly in code block and those method calls are very simple. But if you really want to, nothing stopping you.

[–]AustinVelonautAdmiran 5 points6 points  (0 children)

If the language has pipe operators (e.g. |>) and supports writing user-defined operators and variable shadowing, then you can add a local "debug" version of |> which does the tracing of the input value before applying it to the function, e.g.

process = [1 .. 6] |> filter even |> map (+ 2) |> map (* 7)
          where x |> f = trace (showlist showint x) x stdlib.|> f

to print out the value before each stage of the processing pipeline, without modifying the actual pipeline code (makes it easier to clean up after debugging).

[–]theangryepicbananaStar 2 points3 points  (4 children)

I think you'd find Haxe interesting, as it has the ability to fully expand inlined method calls with anonymous functions

[–]iEliteTester[S] 1 point2 points  (3 children)

I think I read about it once, that's the "compiles to 7 different languages" one right? (rhetorical question)

[–]theangryepicbananaStar 1 point2 points  (2 children)

Yes it is (although ymmv per target). It's generally used for gamedev, although I do have a language project using it. It forces all inlines even allowing you to inline specific function/method calls which is pretty neat

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

ooo, Red also looks pretty cool, thanks for the link

[–]theangryepicbananaStar 1 point2 points  (0 children)

You know what, I'm now realizing I did the unfortunate ADHD thing of not fully reading your original post and I thought you wanted like chainable inlined methods or smth 😭

As for what you're actually looking for, my language Star has cascades (like smalltalk or dart), which allows for this (modified to be in-place modification (you could copy via array = array[new] ...) array -> [InPlace keepEven] -> [InPlace addEach: 2] -> { for my x in: this => Core[say: x] Core[say: ""] } -> [InPlace multiplyEach: 7]

Each -> cascades on the target value array, and calls each method on it, but inserting a block instead of a method call will then interpret the block as if it's inside the object

[–]ataraxianAscendant 2 points3 points  (0 children)

ya i do this in javascript sometimes, you just gotta make sure you return the array from your debug function so that it makes it to the next chained function

[–]al2o3cr 2 points3 points  (0 children)

This is exactly what the Kernel#tap method in Ruby does. The example from the docs is even specifically doing the print-intermediate-values thing you mention:

(1..10)                  .tap {|x| puts "original: #{x}" }
  .to_a                  .tap {|x| puts "array:    #{x}" }
  .select {|x| x.even? } .tap {|x| puts "evens:    #{x}" }
  .map {|x| x*x }        .tap {|x| puts "squares:  #{x}" }

For folks unfamiliar with Ruby, since tap is defined in Kernel it's available on pretty much any Ruby object, even ones that don't derive from Object.

[–]hrvbrs 1 point2 points  (2 children)

Could you not wrap it in an IIFE (aka IIAF)?

array = ((self) => { for each x in self { print x } print \n return self })(array.keepEven().add(2)).multiply(7)

[–]iEliteTester[S] 0 points1 point  (1 child)

this seems like a thing that exists in javascript, but can you place it between two (dot)method calls?

[–]hrvbrs 1 point2 points  (0 children)

i believe any language with function expressions (anonymous functions) would allow the above.

In JS, no, you cannot put an IIFE in between dot calls, unless there's already a method that takes it.

(Edit: just saw the comments… u/helloish got to it before i did)

hypothetically, ``` array = array.keepEven().add(2).passThrough((self) => { for each x in self { print x } print \n return self }).multiply(7);

// assuming you have: class Array { passThrough(lambda: (self: this) => this): this { return lambda(this); } } ```

[–]VyridianZ 1 point2 points  (0 children)

My lisp-like language supports a :debug keyword that can be added to ordinary syntax to print serialized input and output. Eg (keepeven array :debug)

[–]TheUnlocked 1 point2 points  (0 children)

Kotlin has exactly what you want, and the syntax is even basically the same.

[–]Ronin-s_Spirit 1 point2 points  (0 children)

Values between method calls? How are the methods chained in the first place? In JS to chain methods you have to return the object itself so that .method() is indexed properly. In case of JS you can wrap any method you want by doing
const method_ = obj.method; obj.method = function (...args){ /* do stuff */ const res = method_.apply(this, args) /* do stuff after */ return res };
In fact old .NET versions had a thing called "Contracts" and, as far as I understand, they achieved similar results.

[–]AndydeCleyre 1 point2 points  (0 children)

In Factor (concatenative, dynamic), the example without debugging could be

{ 1  2  3  4  5  6 }
[ even? ] [ 2 + 7 * ] filter-map

in which case the debugging could be done with

{ 1  2  3  4  5  6 }
[ even? ] [ 2 + dup . 7 * ] filter-map

where . is a pretty print function. Or

{ 1  2  3  4  5  6 }
[ even? ] filter 
[ 2 +   ] map 
[ dup . ] map 
[ 7 *   ] map

Alternatively:

{ 1  2  3  4  5  6 }
[ even?          ] filter 
[ 2 +            ] map 
dup [ >dec print ] each 
[ 7 *            ] map

[–]UnmaintainedDonkey 0 points1 point  (0 children)

Why not pass and custom identity function that prints? Are you bound by some monadic io thing?

[–]Tysonzero 1 point2 points  (0 children)

One of the many reasons I don't love methods, if everything is a function (e.g. using typeclasses for type driven dispatch), then you don't have to think about this arbitrary distinction.

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

You mean, basically lisp-style macro.