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

Permit function returns in generic instantiation, not just types #246

Open
jrfondren opened this issue Dec 7, 2023 · 0 comments
Open

Permit function returns in generic instantiation, not just types #246

jrfondren opened this issue Dec 7, 2023 · 0 comments

Comments

@jrfondren
Copy link

Feature motivation

If you want to write a function that works on a span(integer), it's trivial: you just accept the value and use it. If you want to write a function that works on a span of any type, you have higher-effort options with some downsides:

  1. edit the function into the make_spanT that creates all of the specialized functions. (downside: your function can't go where you wanted to put it. You have to 'upstream' it.)

  2. accept an auto instead (downside: callsite type inference is less helpful, you can start passing arrays when you wanted to construct a span() and pass that)

  3. write a concept that handles type inference and has appropriate errors (downside: this is more work, it's less clear what you're intending)

  4. write your own generic type and have callers specialize it to pull out the function (downside: lots of ceremony for the caller to do vs. calling a function, and the caller very easily hits "cannot do type cast on generics" errors)

require 'span'
## local function make_spanlength(T)
  local r = @record{}
  function r.call(sp: span(#[T]#))
    return #sp
  end
  ## return r
## end
local spanlength: type = #[generalize(make_spanlength)]#

print((@spanlength(@byte)).call('hello'))

Feature description

Skip the need for the dummy record and its metafield:

require 'span'
## local function make_spanlength(T)
  local function spanlengthT(sp: span(#[T]#))
    return #sp
  end
  ## return spanlengthT
## end
local spanlength: type = #[generalize(make_spanlength)]#

print(spanlength(byte)('hello'))

Currently that's a "cannot do type cast on generics" error, but it seems like reasonable syntax. Trying to avoid that error exposes others:

print(@spanlength(byte)('hello'))  -- unclosed parenthesis
print((@spanlength(byte))('hello')) -- error in generic instantiation: expected a symbol holding a type in generic return, but got something else

This feels more like D or C++ style generics with a second set of arguments, but it retains the flexibility of the current system to specialize arbitrarily over multiple types or values.

Alternatives

  1. Accepting auto in generic parameters. This is natural for the simple case, but you still have to add explicit checks if you want, for example, two spans of the same inner type.
require 'span'
local function spanlength(sp: span(auto))
  return #sp
end
print(spanlength('hello'))
  1. Zig-like generics: permitting comptime type arguments to appear elsewhere in the function parameters. This is also flexible but more verbose when the type inferencer might not need the help.
require 'span'
local function spanlength(T: type <comptime>, sp: span(T))
  return #sp
end
print(spanlength(byte, 'hello'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant