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

Improve the orthogonality of implicit generics with typeclasses #23576

Open
jrfondren opened this issue May 5, 2024 · 0 comments
Open

Improve the orthogonality of implicit generics with typeclasses #23576

jrfondren opened this issue May 5, 2024 · 0 comments

Comments

@jrfondren
Copy link
Contributor

Summary

Between logically identical uses of typeclasses, some work as expected and some lead to mysterious type resolution errors.

Description

when defined(nodistinct):
  # type mismatch: T1 can only resolve to one type per instantiation
  type T1 = int | seq[int]
  proc p1(a, b: T1): int =
    when a is int: result.inc a else: result.inc a[0]
    when b is int: result.inc b else: result.inc b[0]
elif defined(withdistinct):
  # this works
  type T1 = int | seq[int]
  proc p1(a, b: distinct T1): int =
    when a is int: result.inc a else: result.inc a[0]
    when b is int: result.inc b else: result.inc b[0]
else:
  # this also works, even though it's an equivalent-looking expansion of the
  # 'nodistinct' case that doesn't work
  proc p1(a: int | seq[int], b: int | seq[int]): int =
    when a is int: result.inc a else: result.inc a[0]
    when b is int: result.inc b else: result.inc b[0]
echo "p1: ", p1(1, @[2])
echo "p1: ", p1(@[1], 2)
echo "p1: ", p1(@[1], @[2])
# If you realize you're repeating an implicit typeclass in your code, and
# decide to give it a name rather than repeat yourself so much, your code can
# suddenly fail to typecheck.

# suggestion: make the 'nodistinct' case behave the same as 'withdistinct',
# or give a clear error when a call would only typecheck with distinct

when defined(tupled):
  # type mismatch: the implicit typeclass can only resolve to one type per
  # instantiation - even though very similar code just worked, above!
  proc p2(a: (int | seq[int], int | seq[int])): int =
    when a[0] is int: result.inc a[0] else: result.inc a[0][0]
    when a[1] is int: result.inc a[1] else: result.inc a[1][0]
elif defined(tupled_distinct):
  # type mismatch: the distinct-typeclass workaround also stops working
  type T2 = int | seq[int]
  proc p2(a: (distinct T2, distinct T2)): int =
    when a[0] is int: result.inc a[0] else: result.inc a[0][0]
    when a[1] is int: result.inc a[1] else: result.inc a[1][0]
else:
  # this works
  proc p2[A, B: int | seq[int]](a: (A, B)): int =
    when a[0] is int: result.inc a[0] else: result.inc a[0][0]
    when a[1] is int: result.inc a[1] else: result.inc a[1][0]
echo "p2: ", p2((1, @[2]))
echo "p2: ", p2((@[1], 2))
echo "p2: ", p2((@[1], @[2]))
# suggestion: make typeclasses behave the same with tuples as without them

import sets
when defined(higher):
  # Error: undeclared field: 'key' for type sets.OrderedKeyValuePair
  func p4(list: OrderedSet[int | string]): string =
    for x in list: return $x
elif defined(higher_named):
  # this works
  type T4 = int | string
  func p4(list: OrderedSet[T4]): string =
    for x in list: return $x
elif defined(higher_explicit):
  # this works
  func p4[T: int | string](list: OrderedSet[T]): string =
    for x in list: return $x
else:
  # this works
  func p4(list: OrderedSet[int] | OrderedSet[string]): string =
    for x in list: return $x
echo "p4: ", p4(toOrderedSet([1, 2]))
echo "p4: ", p4(toOrderedSet(["1", "2"]))
# suggestion: make all of these alternatives equivalent

Alternatives

No response

Examples

No response

Backwards Compatibility

No response

Links

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant