Polymorphic vs. ordinary variants in reason

综合技术 klipse.tech (源链)

Introduction

Variants are one of the coolest features of reasonml .

In the official documentation , they mention a limitation of the variants (also called ordinary variants):

A function can’t accept an arbitrary constructor shared by two different variants.

They also mention that this is possible to overcome this limitation using polymorphic variants.

The purpose of this article is to expose the limitations of the ordinary variants and to see how polymorphic variants overcome this limitation. We hope that the examples we bring with dogs and tulips will make the reading of this article somewhat enjoyable.

Ordinary Variants – brief recap

Let’s say you have an animal variant

type animal = 
| Dog
| Cat

And you want to write a function that stringifies an animal .

let string_of_animal = x => 
switch (x)  {
|Dog => "dog"
|Cat => "cat"
}

Now, a Dog is a “dog” and a Cat is a “cat”:

"The " ++ string_of_animal(Dog) ++ " bites the " ++  string_of_animal(Cat)

So far so good.

Now let’s do the same with flowers:

type flower =
| Rose
| Tulip;

let string_of_flower = x => 
switch (x)  {
|Rose => "rose"
|Tulip => "tulip"
};

let a = "The " ++ string_of_flower(Rose) ++ " is more beautiful than the " ++  string_of_flower(Tulip);

The limitation of Variants

Now what happens if you try to write a function that stringifies both flowers and animals?

let string_of_flower_or_animal = x =>
switch (x)  {
|Rose => "rose"
|Tulip => "tulip"
|Dog => "dog"
|Cat => "cat"
};

The constructor Dog doesn’t belong to type flower and in that case ocaml doesn’t create a flower_or_animal type on the fly!

Another limitation of ordinary variants is that you cannot mix elements of types animal and flower in a list or in an array:

let a = [Dog, Cat, Rose, Tulip]

Welcome to the world of polymorphic variants!

Polymorphic variants

Syntactically, polymorphic variants are distinguished from ordinary variants by the leading backtick:

let myDog = `Dog;

Note that unlike ordinary variants, polymorphic variants can be used without an explicit type declaration. Their type is inferred automatically.

Of course, it works also with variants that are parametrized:

let myNumber = `Int(4)

Now, let’s see how to write our string_of_animal_or_flower function with polymorphic types:

let string_of_flower_or_animal = x =>
switch (x)  {
|`Rose => "rose"
|`Tulip => "tulip"
|`Dog => "dog"
|`Cat => "cat"
};

Note that the system has automatically inferred the type of the function argument: it’s [< `Cat | `Dog | `Rose | `Tulip ] . You probably wonder what is the meaning of the < sign.

Before answering that question, let’s see how polymorphic variants allow us to mix elements of different types in a list:

let myNature = [`Dog, `Cat, `Rose, `Tulip]

Now, the type of the list is: [> `Cat | `Dog | `Rose | `Tulip ] list .

Upper and lower bounds

Now it’s time to explain what is the meaning of < and > in the context of polymorphic variants.

The > at the beginning of a variant type marks the type a being open to combination with other variant types. We can read the type [> `Cat | `Dog | `Rose | `Tulip] as describing a variant whose tags include Cat, Dog, Rose and Tulip, but may include more tags as well.

In other words, you can roughly translate > to mean: “these tags or more”.

Indeed, we are allowed concatenate list of animals and list of flowers:

let myAnimals = [`Dog, `Cat];
let myFlowers = [`Rose, `Tulip];
let myThings = List.concat([myAnimals, myFlowers]);

The < at the beginning of a variant type means “these tags or less”. For instance, in our string_of_flower_or_animal function defined above, the argument has been inferred to be of type [< `Cat | `Dog | `Rose | `Tulip ] . Indeed the function has no way to deal with values that have tags other that Cat, Dog, Rose and Tulip.

Conclusion

You might now ask yourself why not always use polymorphic variants.

The answer is that the flexibility of polymorphic variant comes at a price.

  1. They are more complex that ordinary variants
  2. They are less likely to catch bugs that ordinary variants – precisely because of the flexxibility they allow
  3. They are a bit heavier and less performant that ordinary variants

Be sure to read this chapter of Real World Ocaml to go deeper with your understanding of ordinary and polymorphic variants. At the end of this chapter they explain with grat details what are the advantages and disadvantages of polymorphic variants over ordinary variants.

您可能感兴趣的

Reviving WWW::Shorten Last July I wrote a post threatening to cull some of my unused CPAN modules. Some people made sensible comments and I never got round to removing ...
8 Reasons Why Code Breaks Code is Untested Changing code in one place all too often breaks code in a (seemingly) unrelated place. No matter how experienced you might be: you ...
Divide And Conquer the most powerful concept in pr... Divide and Conquer is a name given to a group of algorithms that take a problem and then solves it recursively (recursion is a programming concep...
The Joy of Coding – Episode 111 The Joy of Coding - Episode 111Unscripted, unplanned, uncensored, and true to life, watch what a Firefox Desktop engineer does to close b...
Coroutine for C发布 有一段时间没发博客了,10月份实验室布置了个任务,写个pl0文法的语言,差不多赶了1个月,编译原理这块一直都是挺有趣的,当然也是有一定难度的,有机会我一定会往编译原理这个分类中添加几篇文章。 好,言归正传。之前发了2篇关于协程的文章,都只是提供了一个大概的思想,并没有给出具体的实现。似乎缺少了实...
责编内容来自:klipse.tech (源链) | 更多关于

阅读提示:酷辣虫无法对本内容的真实性提供任何保证,请自行验证并承担相关的风险与后果!
本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » Polymorphic vs. ordinary variants in reason



专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录