Skip to content

Commit

Permalink
Better type unifier
Browse files Browse the repository at this point in the history
  • Loading branch information
julianhyde committed Mar 8, 2024
1 parent b7d515b commit 4e0b412
Show file tree
Hide file tree
Showing 8 changed files with 625 additions and 257 deletions.
33 changes: 1 addition & 32 deletions src/main/java/net/hydromatic/morel/compile/Inliner.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@
import net.hydromatic.morel.type.Binding;
import net.hydromatic.morel.type.FnType;
import net.hydromatic.morel.type.PrimitiveType;
import net.hydromatic.morel.type.TupleType;
import net.hydromatic.morel.type.Type;
import net.hydromatic.morel.type.TypeSystem;
import net.hydromatic.morel.type.TypeVar;

import com.google.common.collect.ImmutableMap;
import org.checkerframework.checker.nullness.qual.Nullable;
Expand Down Expand Up @@ -159,7 +157,7 @@ public static Inliner of(TypeSystem typeSystem, Environment env,
return let;
}
final UnaryOperator<Type> typeTransform =
unify(apply2.fn.type, apply.fn.type);
TypeUnifier.unify(typeSystem, apply2.fn.type, apply.fn.type, true);
return substituteType(let, typeTransform);
}
return apply2;
Expand Down Expand Up @@ -265,35 +263,6 @@ protected CopyingShuttle(TypeSystem typeSystem,
}
}

/** Given a source and destination type, returns the substitution
* that converted source to destination.
*
* <p>Examples:
* <ul>
* <li>{@code unify(var(0), int)} &rarr; [var(0) &rarr; int]
* <li>{@code unify(var(0), bool list)} &rarr; [var(0) &rarr; bool list]
* <li>{@code unify(var(0) * var(0), int * int)} &rarr; [var(0) &rarr; int]
* </ul>
*/
UnaryOperator<Type> unify(Type fromType, Type toType) {
if (fromType instanceof TypeVar) {
return t -> t.equals(fromType) ? toType : t;
}
if (fromType instanceof TupleType
&& toType instanceof TupleType) {
// This is wrong.
return unify(((TupleType) fromType).argType(0),
((TupleType) toType).argType(0));
}
if (fromType instanceof FnType
&& toType instanceof FnType) {
// This is wrong.
return unify(((FnType) fromType).resultType,
((FnType) toType).resultType);
}
// And so is this
return t -> t;
}
}

// End Inliner.java
119 changes: 119 additions & 0 deletions src/main/java/net/hydromatic/morel/compile/TermToTypeConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Licensed to Julian Hyde under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Julian Hyde licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package net.hydromatic.morel.compile;

import net.hydromatic.morel.type.Type;
import net.hydromatic.morel.type.TypeSystem;
import net.hydromatic.morel.type.TypeVar;
import net.hydromatic.morel.util.PairList;
import net.hydromatic.morel.util.Unifier;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;

import static net.hydromatic.morel.util.Pair.forEach;
import static net.hydromatic.morel.util.Static.transform;
import static net.hydromatic.morel.util.Static.transformEager;

/**
* Visitor that converts type terms into actual types.
*/
class TermToTypeConverter implements Unifier.TermVisitor<Type> {
private final TypeSystem typeSystem;
private final Function<Unifier.Variable, Unifier.Term> varToTerm;
private final Function<Unifier.Variable, TypeVar> varToType;

TermToTypeConverter(TypeSystem typeSystem,
Function<Unifier.Variable, Unifier.Term> varToTerm,
Function<Unifier.Variable, TypeVar> varToType) {
this.typeSystem = typeSystem;
this.varToTerm = varToTerm;
this.varToType = varToType;
}

@Override public Type visit(Unifier.Sequence sequence) {
final Type type;
switch (sequence.operator) {
case TypeResolver.FN_TY_CON:
assert sequence.terms.size() == 2;
final Type paramType = sequence.terms.get(0).accept(this);
final Type resultType = sequence.terms.get(1).accept(this);
return typeSystem.fnType(paramType, resultType);

case TypeResolver.TUPLE_TY_CON:
assert sequence.terms.size() != 1;
final List<Type> argTypes =
transformEager(sequence.terms, term -> term.accept(this));
return typeSystem.tupleType(argTypes);

case TypeResolver.LIST_TY_CON:
assert sequence.terms.size() == 1;
final Type elementType = sequence.terms.get(0).accept(this);
return typeSystem.listType(elementType);

case "bool":
case "char":
case "int":
case "real":
case "string":
case "unit":
default:
type = typeSystem.lookupOpt(sequence.operator);
if (type != null) {
if (sequence.terms.isEmpty()) {
return type;
}
final List<Type> types =
transform(sequence.terms, t -> t.accept(this));
return typeSystem.apply(type, types);
}
if (sequence.operator.startsWith(TypeResolver.RECORD_TY_CON)) {
// E.g. "record:a:b" becomes record type "{a:t0, b:t1}".
final List<String> argNames = TypeResolver.fieldList(sequence);
if (argNames != null) {
final PairList<String, Type> argNameTypes = PairList.of();
final AtomicBoolean progressive = new AtomicBoolean(false);
forEach(argNames, sequence.terms, (name, term) -> {
if (name.equals(TypeResolver.PROGRESSIVE_LABEL)) {
progressive.set(true);
} else {
argNameTypes.add(name, term.accept(this));
}
});
return progressive.get()
? typeSystem.progressiveRecordType(argNameTypes)
: typeSystem.recordType(argNameTypes);
}
}
throw new AssertionError("unknown type constructor "
+ sequence.operator);
}
}

@Override public Type visit(Unifier.Variable variable) {
final Unifier.Term term = varToTerm.apply(variable);
if (term != null) {
return term.accept(this);
}
return varToType.apply(variable);
}
}

// End TermToTypeConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package net.hydromatic.morel.compile;

import net.hydromatic.morel.type.Type;
import net.hydromatic.morel.type.TypeVar;
import net.hydromatic.morel.util.PairList;
import net.hydromatic.morel.util.Unifier;

import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import static net.hydromatic.morel.util.Pair.forEach;
import static net.hydromatic.morel.util.Static.transform;
import static net.hydromatic.morel.util.Static.transformEager;

/**
* Visitor that converts type terms into actual types.
*/
class TermToTypeConverter0
implements Unifier.TermVisitor<Type> {
private final TypeMap typeMap;

TermToTypeConverter0(TypeMap typeMap) {
this.typeMap = typeMap;
}

public Type visit(Unifier.Sequence sequence) {
final Type type;
switch (sequence.operator) {
case TypeResolver.FN_TY_CON:
assert sequence.terms.size() == 2;
final Type paramType = sequence.terms.get(0).accept(this);
final Type resultType = sequence.terms.get(1).accept(this);
return typeMap.typeSystem.fnType(paramType, resultType);

case TypeResolver.TUPLE_TY_CON:
assert sequence.terms.size() != 1;
final List<Type> argTypes =
transformEager(sequence.terms, term -> term.accept(this));
return typeMap.typeSystem.tupleType(argTypes);

case TypeResolver.LIST_TY_CON:
assert sequence.terms.size() == 1;
final Type elementType = sequence.terms.get(0).accept(this);
return typeMap.typeSystem.listType(elementType);

case "bool":
case "char":
case "int":
case "real":
case "string":
case "unit":
default:
type = typeMap.typeSystem.lookupOpt(sequence.operator);
if (type != null) {
if (sequence.terms.isEmpty()) {
return type;
}
final List<Type> types =
transform(sequence.terms, t -> t.accept(this));
return typeMap.typeSystem.apply(type, types);
}
if (sequence.operator.startsWith(TypeResolver.RECORD_TY_CON)) {
// E.g. "record:a:b" becomes record type "{a:t0, b:t1}".
final List<String> argNames = TypeResolver.fieldList(sequence);
if (argNames != null) {
final PairList<String, Type> argNameTypes = PairList.of();
final AtomicBoolean progressive = new AtomicBoolean(false);
forEach(argNames, sequence.terms, (name, term) -> {
if (name.equals(TypeResolver.PROGRESSIVE_LABEL)) {
progressive.set(true);
} else {
argNameTypes.add(name, term.accept(this));
}
});
return progressive.get()
? typeMap.typeSystem.progressiveRecordType(argNameTypes)
: typeMap.typeSystem.recordType(argNameTypes);
}
}
throw new AssertionError("unknown type constructor "
+ sequence.operator);
}
}

public Type visit(Unifier.Variable variable) {
final Unifier.Term term = typeMap.substitution.resultMap.get(variable);
if (term == null) {
return typeMap.typeVars.computeIfAbsent(variable.toString(),
varName -> new TypeVar(typeMap.typeVars.size()));
}
return term.accept(this);
}
}
Loading

0 comments on commit 4e0b412

Please sign in to comment.