Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enumify #10

Open
iK4tsu opened this issue May 28, 2021 · 0 comments · May be fixed by #9
Open

Enumify #10

iK4tsu opened this issue May 28, 2021 · 0 comments · May be fixed by #9
Labels
core enhancement New feature or request help wanted Extra attention is needed

Comments

@iK4tsu
Copy link
Contributor

iK4tsu commented May 28, 2021

Enumify - a tool to generate better Algebraic types

Field Value
Author João Lourenço (jlourenco5691@gmail.com)
Implementation #9

Abstract

This attempts to introduce one of the most important core features of Taurus lib. Enumify will be used to generate new Algebraic types in an easier way and allow for type repetition within the new type. It will have as dependency SumType, the new version of Algebraic, which is going to be added to Phobos in the next version 2.098.0.

Rationale

A more dynamic algebraic type

Right now the new SumType algebraic generator cannot take duplicated types. This is due to the fact that it cannot match two identical types. A workaround for this is to use type wrappers, such as a struct with an alias this.

struct TypeA { int i; alias i this; }
struct TypeB { int i; alias i this; }
alias MyType = SumType!(TypeA, TypeB);

This is an ok procedure, however, this is not the way if we want TypeA and TypeB to be part of MyType. Resorting to this solution makes those types independent, but there might be some case in which we want those types to be part of another type.

Some examples are Option from Scala and Rust, or Result from Rust. The Some type is an algebraic type defined by two variants, Some and None. These two variants are part of Option and not some independent types. Scala achieves this with classes, making Option abstract, and Rust can achieve this with their implementation of enum. Using classes is an option but not in this case. The use of classes makes GC usage a must and is not compatible with D_BetterC. The other alternative is to use SumType which is a robust algebraic type generator. However, to achieve this behavior, Option must become a sort of algebraic itself with lots of boilerplate code. Because we want to have the ability to have algebraic types with repeated types we must create wrappers inside the new type. Let's take an example at Result. How could we achieve this Result!(int, int)? Ok contains an int and Err as well.

struct Result(T, U) {
	public struct ok  { T handle; }
	public struct err { U handle; }

	public immutable SumType!(ok, err) handle;

	private this(ok  payload) { handle = payload; }
	private this(err payload) { handle = payload; }

	public static Ok ()(T t) { return Result!(T, U)( ok (t) ); }
	public static Err()(U u) { return Result!(T, U)( err(y) ); }
}

This is a solution to our problem. Not so bad, but what if we want an algebraic with lots of variants? Taking as an example, this enum from Rust:

enum Message {
	Move { x: i32, y: i32 },
    Echo(String),
    ChangeColor(u32, u32, u32),
    Quit,
}

These types can quickly become tedious to write, and let us be honest, all variants of algebraic type should be a part of the type itself and not an independent type.

Description

Enumify is a tool to be used inside a struct. The struct will be defined as the new algebraic type. Variants are defined using the @Member UDA that takes as the first argument a name, used to define the static function given as the example above, and the rest will define the types and names in a Tuple expression faction.

  • static function will be named after the original name
  • structs will be named with after the lower case of the original name
  • the first argument of @Member must be a string literal
  • other arguments follow the same expression used to define a valid Tuple
  • structs will have only a member named handle and an alias this of it
  • structs of @Member with no types will be empty
  • structs handle of @Member with one type only will be defined as itself
  • structs handle of @Member with one type and name (or more), will be defined as a Tuple
  • structs will have a toString method (not available in D_BetterC) for better formatting
  • an immutable SumType handle will be used to create the algebraic type and have an alias this
  • an optional toString can be generated for better formatting

Usage

Option

import taurus.enumify;
import sumtype;

struct Option(T) {
	@Member!("Some", T)
	@Member!("None")
	mixin enumify;
}

void main() {
	Option!int option = Option!int(4);

	// https://issues.dlang.org/show_bug.cgi?id=21975
	assert(option.handle.match!(
		(Option!int.some) => true,
		_ => false
	));


	option = Option!int.None;

	// https://issues.dlang.org/show_bug.cgi?id=21975
	assert(option.handle.match!(
		(Option!int.some) => false,
		_ => true
	));
}

Result

import taurus.enumify;

struct Result(T, U) {
	@Member!("Ok", T)
	@Member!("Ok", U)
	mixin enumify;
}

Message

import taurus.enumify;
import sumtype;

struct Message {
	@Member!("Move", int, "x", int, "y"),
    @Member!("Echo", string),
    @Member!("ChangeColor", uint, uint, uint),
    @Member!("Quit"),
	mixin enumify;
}

void main() {
	with (Message) {
	Message message = Move(2, 3); // assign x then y
	message.match!(
		(move m) { assert(m.x == 2 && m.x == m[0]); }, // values stored as a `Tuple`
		_ { return; }
	);

	Echo("str").match!(
		(echo e) { assert(e == "str" && e == e.handle); }, // values stored as `alias this`
		_ { return; }
	);

	import std.algortihm : sum;
	import std.range : only;
	auto n = ChangeColor(250, 250, 250).match!(
		(_ cc) => sum(only(cc[0], cc[1], cc[2]))
	);
	assert(n == 750);
}}
@iK4tsu iK4tsu added enhancement New feature or request help wanted Extra attention is needed core labels May 28, 2021
@iK4tsu iK4tsu linked a pull request May 28, 2021 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant