ponyc icon indicating copy to clipboard operation
ponyc copied to clipboard

Allow finite recursive type aliases

Open jemc opened this issue 10 years ago • 9 comments

When working on a pure Pony implementation of JSON parsing and encoding, I've run into the following issue:

When compiling:

type JsonNull    is None
type JsonBoolean is Bool
type JsonString  is String
type JsonNumber  is F64
type JsonArray   is Array[JsonEntity]
type JsonObject  is Map[JsonString, JsonEntity]

type JsonEntity is
  ( JsonNull
  | JsonBoolean
  | JsonString
  | JsonNumber
  | JsonArray
  | JsonObject)

I get type aliases can't be recursive errors. I understand that these type aliases are recursive, but not sure if there is another way to implement this other than using recursive types. Is there a way I can refactor these aliases to avoid the compiler warning? Note that I want to avoid using "container" objects that compose the builtin pony ones - I'd like to operate on builtin pony objects directly, just under aliased names. If not, is it possible to lift this restriction in the compiler by detecting the recursion and responding appropriately?

jemc avatar Jul 23 '15 16:07 jemc

Unfortunately there are technical problems with allowing actual recursive aliases. It may well be possible to do this at some point if there's a big call for it, but it won't be easy and so is unlikely to happen any time soon.

The way the standard library implementation of JSON works (which isn't released yet, sorry it will be soon) is to have wrapper types around the complex JSON types (array and object) but not the others.

type JsonType is (F64 | U64 | Bool | None | String | JsonArray | JsonObject)

class JsonArray
  var data: Array[JsonType]

class JsonObject
  var data: Map[String, JsonType]

andymcn avatar Jul 23 '15 16:07 andymcn

@andymcn - Thanks for the info. Also, If you're already working on a pure Pony JSON implementation, I won't spend any more time on mine, then. I was going to offer it for inclusion into the standard library when it was done, but it sounds like there is no need.

Still, I think there are probably other cases where this issue will come up. I've seen it once before when trying to make a function that can print "inspect"-style strings for collection types. I was unable to make a function that accepts nested collections, which is a pretty major limitation. So if in the future it is possible I would like to know about it and I'd like to leave this issue open for now.

jemc avatar Jul 23 '15 16:07 jemc

Looking forward to it, tried to write a TNetString parser/dumper, at the optimization state the compiler throws a illegal machine instruction exception when i don't immediately return from a type match..

match type
| "," => String
| "#" => Integer

values chosen randomly, as soon as a second match is there the optimization phase crashes, when i do it like this it works.

match type
| "," => return String
| "#" => return Integer

values again chosen randomly, only the return matters.

This also happens with

if
elseif
else
end

Ill try to make a repro soonish, but it will be quite long.

Asmod4n avatar Aug 10 '15 14:08 Asmod4n

@Asmod4n, I can't reproduce the problems you mention, but I'm not sure I understand exactly what situations you're talking about.

Please give a complete program (or at least a complete function) that displays this problem and create a new issue for it.

Thanks.

andymcn avatar Aug 10 '15 14:08 andymcn

Cannot write a repro, don't even have the code in my backups :/ It's most likely because i am using a beta version of Xcode.

Asmod4n avatar Aug 10 '15 19:08 Asmod4n

The problem is we are disallowing all type alias recursion, when we should only disallow infinite type alias recursion. For example:

type A is Array[A] // infinite recursion, illegal
type B is Array[C]
type C is (Array[B] | None) // mutual recursion, but legal

Checking this is certainly possible, but non-trivial. But we should do this.

sylvanc avatar Aug 25 '15 11:08 sylvanc

@SeanTAllen - I don't think this should be labelled as "turn into rfc" - it doesn't introduce any new API or concepts, it simply makes the compiler able to understand a class of programs that the current implementation cannot. I'm going to remove that label.

Also, I see this was labelled as "enhancement" before we added the various levels of work for "enhancement" - I'm going to mark it as "ready for work", since @sylvanc has explained what needs to be done, and it's just a matter of someone coming up with a workable implementation.

jemc avatar Sep 22 '16 14:09 jemc

I'm tagging this as "needs discussion during sync" as I'd like to talk about what we need to do to make this happen.

SeanTAllen avatar Aug 20 '17 15:08 SeanTAllen

The first step is to no longer flatten type aliases. Particularly, in names.c:names_typealias - don't do that :) Instead, we should promote type aliases to a first-class thing in the AST.

After that, there will be a significant amount of work adding type aliases as first-class concepts to subtype.c, matchtype.c, and reify.c - and other places as well, although I think those are likely to take the most work. Effectively, type aliases need to be usable wherever we would use a type.

The last step is to detect, disallow, and report infinite recursion. This should be relatively easy. The key I think is that type parameters don't necessarily cause infinite recursion, as in the mutual recursion example above (which should be allowed).

This will also improve error reporting, as errors involving type aliases can be reported with the alias name, rather than the flattened type (which is often tediously long).

sylvanc avatar Jan 10 '18 22:01 sylvanc