This is to gather input and valid use cases that would benefit from supporting parameterized typespecs.
Current implementation can be used like this:
defmodule ParsedBody do .. end # just a struct
defmodule ParseError do ... end # just a struct
defmodule ValidationError do ... end # just a struct
defmodule ParseResult do
use DiscUnion
defunion :ok in ParsedBody.t | :error in ParseError.t
end
defmodule ValidationResult do
use DiscUnion
defunion :ok in ParsedBody.t | :error in ValidationError.t
end
defmodule UsageExample do
require ParseResult
require ValidationResult
def foo do
ParseResult.c! :ok, %ParsedBody{}
ValidationResult.c! :error, %ValidationError{}
end
end
This will generate two different structs/unions with typespecs that will look like:
The proposed idea is to have parametrized types and support for creating specialized version of a more general unions.
Lets say we want a generic Result union and use it with specialized types for different returned data types hold in the same structure - some think akin to polymorphic types in OCaml - than creating separate modules, ParseResult and ValidationResult, and useing our general union, would populate that modules with defdelagtes (the constructor functions) to Result module but with specialized types:
defmodule ParsedBody do .. end # ust a struct
defmodule ParseError do ... end # just a struct
defmodule ValidationError do ... end # just a struct
defmodule Result do
use DiscUnion, type_params: [:content, :error_reason]
defunion :ok in content | :error in error_reason
end
defmodule ParseResult do
use Result, type_params: [content: ParsedBody.t, error_reason: ParseError.t]
end
defmoduule ValidationReslt do
use Result, type_params: [content: ParsedBody.t, error_reason: ValidationError.t]
end
defmodule UsageExample do
require ParseResult
require ValidationResult
def foo do
ParseResult.c! :ok, %ParsedBody{}
ValidationResult.c! :error, %ValidationError{}
end
end
this would creat one struct/union, but with two different proxy modules with specs like this:
The usage in both cases is basically the same, the only exception being what is returned. In the second case, it would be that general %Result{} discriminated union. While in the first case it would be either a %ParseResult{} or %ValidationResult{}.
The big question is: is it worth it? Are there any real-world use cases that would require those parametrized union cases like in the second example? Dialyzer is not a static-typing analysis tool but a success-typing analysis tool so I'm not really sure if it even will be able to benefit from that way of doing things.
This is to gather input and valid use cases that would benefit from supporting parameterized typespecs.
Current implementation can be used like this:
This will generate two different structs/unions with typespecs that will look like:
The proposed idea is to have parametrized types and support for creating specialized version of a more general unions. Lets say we want a generic
Result
union and use it with specialized types for different returned data types hold in the same structure - some think akin to polymorphic types in OCaml - than creating separate modules,ParseResult
andValidationResult
, anduse
ing our general union, would populate that modules withdefdelagte
s (the constructor functions) toResult
module but with specialized types:this would creat one struct/union, but with two different proxy modules with specs like this:
The usage in both cases is basically the same, the only exception being what is returned. In the second case, it would be that general %Result{} discriminated union. While in the first case it would be either a %ParseResult{} or %ValidationResult{}.
The big question is: is it worth it? Are there any real-world use cases that would require those parametrized union cases like in the second example? Dialyzer is not a static-typing analysis tool but a success-typing analysis tool so I'm not really sure if it even will be able to benefit from that way of doing things.