anchor.diff:      ct:pal("~w", [Substitutions]),
       throw({subst_error, unknown_expression}),
       exit(foobar).
  +
  +% type equality minus line number
  +equal_types(Left, Right)  when is_list(Left) andalso is_list(Right) ->
  +    lists:all(fun ({L, R}) ->
  +                      equal_types(L, R)
  +              end, lists:zip(Left, Right));
  +equal_types({type,_, Type, LeftList}, {type,_, Type, RightList} ) ->
  +    equal_types(LeftList, RightList);
  +equal_types(_, _) ->
  +    false.
  +
  
positive.code: parse_constraints([ConstraintType | Rest], Substitutions) ->
      case ConstraintType of
          {type,_,constraint,[{atom,_,is_subtype},[{var,_,Variable},Type]]} ->
              NewSubstitutions = gb_trees:insert(Variable, Type, Substitutions),
              parse_constraints(Rest, NewSubstitutions);
          _ ->
              ct:pal("unknown constraint ~w", [ConstraintType]),
              parse_constraints(Rest, Substitutions)
  
negative.code:         none -> {var,_Line,VarName}
      end;
  
  substitute_constraints({type, _Line,bounded_fun, [FunType, _Constraints]}, Substitutions) ->
      substitute_constraints(FunType, Substitutions);
  
  % generic types
  