Search code examples
scalagoflowtypetype-systems

How do I declare the signature of a function that must return one of its arguments? (in any language*)


How does one express the signature for a function that must return an argument (or this) that it receives (is called on), in TypeScript? Is there a programming language where this is possible?*

// In TypeScript (or consider it pseudo-code)
class C {
  // EXAMPLE 1 – Not polymorphic
  chainable(x): this                 // MUST not only return some C,
  {}                                 // but the same instance it was called on
}
// EXAMPLE 2
function mutate<T>(a: T[], x): T[]   // MUST return a, not a new Array
{
  /* So that this doesn't compile */ return Array.from(a);
  /* But this is OK */               return a;
}

Conversely, how about a function that must return a new instance?

// EXAMPLE 3
function slice<T>(a: T[], x, y): T[] // MUST return a new Array

❌TypeScript


Go 2?

Would the following contract achieve the above?

contract referentiallyIdentical(f F, p P) {
  f(p) == p
  v := *p
}
type returnsSameIntSlice(type T, *[]int referentiallyIdentical) T
func main() {
  var mutate returnsSameIntSlice = func(a *[]int) *[]int {
    b := []int{2}
    /* Would this compile? */ return &b
    /* This should */         return a
  }
}  

C++20?

Could the above be expressed as a C++ concept?


✅Scala


*Originally, the question was about doing this in TypeScript, but since that isn't possible, I am curious if it is in another language.

Feel free to remove a tag if that language's type system can't express this


Solution

  • You can - in Scala.

    Class with a method returning this.type:

    class C {
      var x = 0 
    
      /** Sets `x` to new value `i`, returns the same instance. */
      def with_x(i: Int): this.type = {
        x = i
        this   // must be `this`, can't be arbitrary `C`
      } 
    }
    

    In-place sort that guarantees to return exactly the same array (doesn't really sort anything here):

    def sortInPlace[A: Ordered](arr: Array[A]): arr.type = {
      /* do fancy stuff with indices etc. */
      arr
    }
    

    If you attempt to return a different array,

    def badSortInPlace(arr: Array[Int]): arr.type = Array(1, 2, 3) // won't compile
    

    you'll get an error at compile time:

    error: type mismatch;
    found   : Array[Int]
    required: arr.type
          def badSortInPlace(arr: Array[Int]): arr.type = Array(1, 2, 3)
                                                               ^
    

    This is called a singleton type, and is explained in the spec.