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

Restriction of using row polymorphism for the effect system #1177

Open
thwfhk opened this issue May 24, 2023 · 0 comments
Open

Restriction of using row polymorphism for the effect system #1177

thwfhk opened this issue May 24, 2023 · 0 comments

Comments

@thwfhk
Copy link
Contributor

thwfhk commented May 24, 2023

This blog gives a good example showing the restriction to expressiveness of using row polymorphism for the effect system.
In Links, the same problem happens with some slightly modification.

Consider the following recursive function iter which works well in Links.

links> @set show_quantifiers true;
links> @set show_kinds hide;
links> fun iter(n)(f)(x) {if (n==0) {x} else {iter(n-1)(f)(f(x))}};
iter = fun : forall a::Row,b::Type,c::Row,d::Row.(Int) -a-> ((b) ~c~> b) -d-> (b) ~c~> b
links> iter(2);
fun : ((a) ~b~> a) -> (a) ~b~> a

The iter allows its arrows to have different row variables. Thus, iter(2) can take any impure function. This is because Links uses a trick to enable a restricted form of row polymorphic recursion for curried multi-parameters recursive functions. Approximately, Links forces the iter to be bound with a row polymorphic type forall a:Row,d::Row. (...) -a-> (...) -d-> (...) in the context.

However, if we slightly change the definition of iter to nested function literals, the trick fails.

links> fun myiter() {fun(n) {fun(f) {fun(x) {if (n==0) {x} else {myiter()(n-1)(f)(f(x))}}}}};
myiter = fun : forall a::Row,b::Type.() ~a~> (Int) ~a~> ((b) ~a~> b) ~a~> (b) ~a~> b
links> myiter()(2);
fun : ((a) {}~> a) {}~> (a) {}~> a

Now, iter requires its arrows to use the same row variable! As a result, iter(2) can only take pure functions, which is too restrictive.

Of course, we can fix this problem by writing out the signature of myiter to enable polymorphic recursion.

links> sig myiter : () -> (Int) -> ((b) ~c~> b) -> (b) ~c~> b
...... fun myiter() {fun(n) {fun(f) {fun(x) {if (n==0) {x} else {myiter()(n-1)(f)(f(x))}}}}};
myiter = fun : forall a::Row,b::Row,c::Type,d::Row,e::Row.() -a-> (Int) -b-> ((c) ~d~> c) -e-> (c) ~d~> c
links> myiter()(2);
fun : ((a) ~b~> a) -> (a) ~b~> a

To solve it fundamentally, we could probably either develop better approaches to row polymorphic recursion or add row subtyping, as what are suggested in the blog.

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