# \[RFC\] Undefined Type

**URL:** <https://forum.crystal-lang.org/t/rfc-undefined-type/2695>\
**Category:** Crystal Contrib\
**Created:** [November 22, 2020, 1:16am UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695 "2020-11-22T01:16:24Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [November 22, 2020, 1:16am UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/1 "2020-11-22T01:16:24Z")

</div>

So I had the idea of introducing a new `undefined` type/value with the purpose of allowing the default values of a method to be invoked, even if a value was provided. For example:

```auto
class Example
  def self.from_range(range : Range, message : String = "Not in range")
    new range.begin, range.end, message
  end
  
  def initialize(@min : Int32, @max : Int32, @message : String = "Not in range"); end
end
 
Foo.from_range (1..10)

```

In order for default `message` property to be used when using the `from_range` constructor, it has to be duplicated on both definitions. WIth this proposal you would be able to something like:

```auto
class Example
  def self.from_range(range : Range, message : String = undefined)
    new range.begin, range.end, message
  end
  
  def initialize(@min : Int32, @max : Int32, @message : String = "Not in range"); end
end
 
Foo.from_range (1..10)

```

When _not_ providing a `message` to `from_range` the value of the `message` argument is `undefined`; thus when the actual constructor sees `undefined` it knows to treat that value as if it wasn’t provided at all, resulting in the `message` being `Not in range`. However if you _do_ provide a `message` to `from_range` the argument would be the passed in message, resulting it that value being used.

Currently this _is_ kinda possible by doing something like:

```auto
class Example
  def self.from_range(range : Range, message : String? = nil)
    new range.begin, range.end, message
  end
  
  def initialize(@min : Int32, @max : Int32, message : String? = nil)
    @message = message || "Not in range"  
  end
end
 
Foo.from_range (1..10)

```

However I don’t like that the `message` argument needs to be made nilable when in fact it technically isn’t and is only nilable for this use case. I’d be down for other solutions as well, like maybe if `nil` is passed as an argument that isn’t nilable and has a default value, just use the default? Not a real big fan of that as it could probably lead to some bugs/surprising behavior that would have normally been caught.

Thoughts?

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [November 22, 2020, 10:10am UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/3 "2020-11-22T10:10:55Z")

</div>

I think Brian mentioned this to me in the past, or we had the same idea. It’s a good idea. The thing is that we have to weight how hard it’s to implement this, how intuitive it is, vs existing solutions that are already possible. For example you could extract the default value into a constant. Or… a bit of duplication is sometimes fine.

For example, what happens when you use message when it wasn’t passed? I guess you get an error the same way if message wasn’t actually passed?

Another way to implement this is by providing two overloads, though I guess that’s more duplication.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [November 22, 2020, 3:13pm UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/4 "2020-11-22T15:13:02Z")

</div>

> [@asterite](#):
>
> It’s a good idea. The thing is that we have to weight how hard it’s to implement this, how intuitive it is, vs existing solutions that are already possible.

Yea for sure. I wouldn’t say its high priority, definitely an after 1.0 thing. I recently learned that TypeScript does this and quite liked the concept.

> [@asterite](#):
>
> For example, what happens when you use message when it wasn’t passed? I guess you get an error the same way if message wasn’t actually passed?

Yea, my thinking was that if you pass `undefined` to something that doesn’t have a default, the compiler could know and error saying like that argument can be `undefined` but does not have a default value.

I guess the semantics of `undefined` would essentially just be like:

1. Allows using the default value of an argument
2. Compile time error if used on an argument without a default
3. Compile time error when using any method on it

> [@asterite](#):
>
> Another way to implement this is by providing two overloads, though I guess that’s more duplication.

I thought of this but it can quickly get messy if you have like more than a handful of optional arguments.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [November 22, 2020, 4:01pm UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/5 "2020-11-22T16:01:26Z")

</div>

Can you post a link to this feature in Typescript?

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [November 22, 2020, 4:09pm UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/6 "2020-11-22T16:09:22Z")

</div>

Sorry, apparently it’s a JavaScript feature.

The last section in [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default\_parameters#Description](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters#Description)

![image](https://canada1.discourse-cdn.com/flex036/uploads/crystal_lang/original/2X/b/bbaaaa80836a691b28c22eecd8e78e9f57f7da0b.png)

> **[Default parameters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters#Passing_undefined_vs._other_falsy_values)**
>
> Default function parameters allow named parameters to be initialized with default values if no value or undefined is passed.

 ![image](https://canada1.discourse-cdn.com/flex036/uploads/crystal_lang/original/2X/4/407329a23e86fe90b7020c4bf7f27d1f5dd7ec46.png)

Just with TypeScript do you get some type safety around it: [https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAWxAG1gBzQTwBQCGAXImCMgEYCmATgDSIUlmW2IC8iAjAJSIDeAKEQjENKlBA0kBRACpGAbkEBfQYIgIAznDRUAdGjgBzPKgwxs+AKwMATDx7LNYHXsMmz6LLjzXHztq6Bkam5j42DOAAJlTAMGBU0QGCQA](https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABAWxAG1gBzQTwBQCGAXImCMgEYCmATgDSIUlmW2IC8iAjAJSIDeAKEQjENKlBA0kBRACpGAbkEBfQYIgIAznDRUAdGjgBzPKgwxs+AKwMATDx7LNYHXsMmz6LLjzXHztq6Bkam5j42DOAAJlTAMGBU0QGCQA). I.e. if you remove the default value, notice it errors because `undefined` isn’t compatible with `number`. But explicitly passing `undefined` when it has a default is fine.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [November 22, 2020, 4:24pm UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/7 "2020-11-22T16:24:56Z")

</div>

That’s a bit different, right? You pass undefined at the call site, not at the method definition.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [November 22, 2020, 5:28pm UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/8 "2020-11-22T17:28:37Z")

</div>

It works similarly when using `undefined` as the default value. However you do have to add `undefined` type to the allowed types:

```auto
function multiply2(a: number, b: number | undefined = undefined) {
    return multiply(a, b);
}

multiply2(5) # => 5

```

---

<div class="post-metadata">

**Author:** ![straight-shoota](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/straight-shoota/32/36_2.png) [@straight-shoota](https://forum.crystal-lang.org/u/straight-shoota)\
**Post date:** [November 22, 2020, 9:29pm UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/9 "2020-11-22T21:29:08Z")

</div>

`undefined` is just JavaScript’s `nil` (more or less). This is not specific to function arguments.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [November 22, 2020, 9:41pm UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/10 "2020-11-22T21:41:10Z")

</div>

How do you figure? They behave differently. E.x. the second screenshot I linked. `undefined` causes the default value be used, `null` does not.

Although they are similar, the semantics between the two are slightly different.

---

<div class="post-metadata">

**Author:** ![jhass](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jhass/32/1779_2.png) [@jhass](https://forum.crystal-lang.org/u/jhass)\
**Post date:** [November 23, 2020, 8:57am UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/11 "2020-11-23T08:57:53Z")

</div>

I think the nilable solution is a perfectly fine solution to the problem. This is not worth the enormous cognitive complexity it adds to the language where you have to learn and keep in mind the difference between `nil` and `undefined` and learn that `undefined` is not what you may know from other languages.

Let’s please start to strive to keep the language semantics simple or to simplify it further. Adding feature after feature does the opposite.

---

<div class="post-metadata">

**Author:** ![KCErb](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/kcerb/32/985_2.png) [@KCErb](https://forum.crystal-lang.org/u/KCErb)\
**Post date:** [November 25, 2020, 3:03pm UTC](https://forum.crystal-lang.org/t/rfc-undefined-type/2695/12 "2020-11-25T15:03:42Z")

</div>

I agree with @jhass here, one of Ruby/Crystal’s great strengths imo is that we _don’t_ add nuance to `nil`. It’s really nice to know that every thing I could add to my program is either not defined or is defined as something in the language. And the language allows exactly one flavor of ‘defined to hold nothing’ =\> `nil`.

I’ve never enjoyed that a variable can be defined to be undefined in javascript

```javascript
a = undefined

```

Modern javascript has tried to help devs with this issue by introducing the idea of “nullish” to include both undefined and null but … it’s a patch over a problem that bites again and again over in that world.
