NamedTuple with optional field

When I defined an alias for a NamedTuple, is it possible to make some of the fields as optional? Does that even make sense?

require "json"

alias Person = NamedTuple(
  name: String,
  email: String?,
)

p1_str = %[{"name": "Foo", "email": "foo@bar.com"}]
p1 = Person.from_json(p1_str)
p p1


p2_str = %[{"name": "Bar"}]
p2 = Person.from_json(p2_str)
p p2

p3_str = %[{"name": "Bar", "email": null}]
p3 = Person.from_json(p3_str)
p p3

The first json string has both “name” and “email” fields, the second json only has a “name” field. (This is a simplified version of responses from an API.) The 3rd one has an email field with null in it.

If I try to run this code without the ? after the String then I get a run-time exception:

# Unhandled exception: Missing json attribute: email

If I include the ? as it is in the example above then I make the field Nil-able and the result will be:

{name: "Foo", email: "foo@bar.com"}
{name: "Bar", email: nil}
{name: "Bar", email: nil}

This means I cant tell apart the 2nd and 3rd cases.

I wonder if it would be possible to make the whole field optional so the result of the code would be:

{name: "Foo", email: "foo@bar.com"}
{name: "Bar"}
{name: "Bar", email: nil}

Yes! You can use a struct, include JSON::Serializable and control the serialization of that struct or class that way. Just don’t use a NamedTuple, and please don’t encourage its use on the course you’ll be giving. At least that’s my advice.

NamedTuple is used to represent named arguments. My advice is to avoid using NamedTuple and instead use regular types.

thanks, so that would have been my other question. What is the recommended way to represent a response from an API? So far I was building up NamedTuples, would using struct be a better option?
Why? Is there some page explaining when to use each one of them. (also: when to use struct vs. class?)

The main issue with NamedTuple is that it’s a single type. If in your response you need to add some business logic apart from decoding JSON, you won’t be able to do that. Or, well, you have to reopen NamedTuple, or maybe use methods that accept named tuples, but it’s less ideal.

Struct vs class, there is https://crystal-lang.org/reference/guides/performance.html#use-structs-when-possible . The title is pretty misleading. Only use structs with small, immutable types.

1 Like