Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add recursive function to class as member

I'm having trouble adding a recursive function to a class.

I can declare the function private no problem above my member declarations using let.

But when I try to make it public using member, it does not compile.

member this.rec mux xs ys =
  match xs with
  | [] -> ys
  | x::xt -> x :: mux ys xt

Thanks for correcting me and pointing me to a proper resource online. I have been reading many tutorials but I can't find this info.

like image 510
happyD Avatar asked Mar 09 '23 22:03

happyD


1 Answers

Member functions are always recursive, no rec keyword required:

member this.mux xs ys =

(and even if there was a rec keyword, it would go before this, same way as private - member rec this.mux ...)

But once you've declared it as a member, you have to reference it as a member as well - i.e. this.mux instead of mux:

member this.mux xs ys =
  match xs with
  | [] -> ys
  | x::xt -> x :: this.mux ys xt

Why member functions are always recursive, while let-bound functions are not

(in response to comment)

let-bound functions can shadow previously defined identifiers. For example:

let f x = x+5
let f x = x-2
let a = f 5   // a = 3, not 10

This is a perfectly legal thing to do (except at top level in modules), and is frequently used for utilitarian purposes, e.g. sanitizing parameters:

let sendEmail email subject body =
   let email = canonicalize email
   ...

Notice how in this function the first thing I do is clean up the email, and then proceed to do whatever (NOTE: this is not "assigning" new value to the parameter email, but defining a whole new value that just happens to have the same name).

This new definition of email is used in the rest of the function instead of the original parameter email. This is called "shadowing".

Now, notice how this new definition of email references the old definition of email. This is only possible because email is not recursive: the compiler knows that the word email within the definition of email refers to the previously defined value, not the one being defined right now.

"But wait" - you say - "what does it even mean for email to be recursive? Doesn't the term "recursive" only apply to functions?". Well, no. Values can be recursive too, but that's a topic for another time. For now, here's a different example:

let notifyUsers sendEmail log =
   let sendEmail name =
      log ("Notifying " + name)
      sendEmail (name + "@contoso.com")

   sendEmail "John"
   sendEmail "Mark"
   sendEmail "Matthew"
   sendEmail "Luke"

In this example, I "shadowed" the function sendEmail with a new definition that logs the name of the user before calling the original sendEmail. If I defined my "new" sendEmail as recursive (i.e. let rec sendEmail name = ...), this program would result in an infinite loop: the function would just endlessly call itself. But because the function is not recursive, it is able to reference previously-defined value of the same name.

Member functions don't have this problem: you can't shadow a class method, that would be meaningless.

This problem is addressed differently in different languages.
For example, in languages with everything mutable by default, this problem doesn't arise at all: you just mutate the values and that's it... Unless you want to change the type, too - then you're screwed.
For another example, in Haskell, all values are always recursive, and shadowing will cause a warning. As a result, people are forced to use ticks or get creative with naming, or even introduce a monad where none is required.

like image 102
Fyodor Soikin Avatar answered Mar 19 '23 10:03

Fyodor Soikin