140 likes | 248 Vues
This paper explores the challenges of type error messages in functional programming, specifically in ML and C++. It examines how the current type error messages can be unhelpful and provides a framework for improving these messages through program analysis. By treating the type checker as an oracle, the approach searches for single AST subtree removals to resolve errors, enhancing message clarity and relevance. Preliminary results show improvements in error diagnostics and precision across various case studies, making type inference and error handling more intuitive for developers.
E N D
SEMINAL: Searching for Type-Error Messages Benjamin Lerner, Dan Grossman, Craig Chambers University of Washington
# let map2 f aList bList = List.map (fun (a, b) -> f a b) (List.combine aList bList);; val map2 : ('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list = <fun> # map2 (fun (x, y) -> x + y) [1;2;3] [4;5;6];; This expression has type int but is here used with type 'a -> 'b Try replacing fun (x, y) -> x + y with fun x y -> x + y Example: ML Curried functions
template <class A, class B> list<B> map(B (*f)(A), list<A> aList); template <class A> list<list<A> > transpose( list<list<A> > aMatrix); map(ptr_fun(transpose<int>), myIntMatrixList); temp.cpp:42: error: no matching function for call to map(std::pointer_to_unary_function<std::list<std::list<int, std::allocator<int> >, std::allocator<std::list<int, std::allocator<int> > > >, std::list<std::list<int, std::allocator<int> >, std::allocator<std::list<int, std::allocator<int> > > > >, std::list<std::list<std::list<int, std::allocator<int> >, std::allocator<std::list<int, std::allocator<int> > > >, std::allocator<std::list<std::list<int, std::allocator<int> >, std::allocator<std::list<int, std::allocator<int> > > > > >&) Try replacing ptr_fun(transpose<int>) with &(transpose<int>) Example: C++ Template Usage temp.cpp:42: error: No match for map( pointer_to_unary_function<list<list<int> >, list<list<int> > >, list<list<list<int> > > &)
What went right? • Easy polymorphic functions • map2 works for any kind of list • transpose works for any kind of matrix • Type inference • Writing types manually is tedious • Type-inference makes generic programming easier • …Even more powerful type-systems need type inference even more.
What went wrong? These type-error messages… • …are not local (particularly ML) • Symptoms <> Problems • …are not intuitive (particularly C++) • Very lengthy types are hard to read • …are not descriptive • Location + types <> Solution • …are due to type inference/instantiation
Related work • Instrument the type-checker, making its messages more informative • But that leads to tight coupling • Implementing a TC is easy • Implementing a production TC is hard • Adding good error messages makes it even harder • Interferes with easy revision or extension of TC • Error messages in TC adds to compiler’s trusted computing base See paper for citations
Our Approach, in one slide • Treats type checker as oracle • Makes no assumptions about the type system • Note: no dependence on how TC works • Tries many variations on program, see which ones work • Must do this carefully – there are too many! • Note: “Variant works” <> “Variant is right” • Ranks successful suggestions, presents results to programmer
Architecture – Searcher Defines the strategy for looking for fixes: • Look for single AST subtree to remove that will make problem “go away” • Replace subtree with “wildcards” • Interesting subtrees guide the search • If removing a subtree worked, try its children … • Often improves on existing messages’ locations • Rely on Enumerator’s suggestions for more detail
Architecture – Enumerator Defines the fixes to be tried: • Try custom attempts for each type of AST node • E.g. Function applications break differently than if-then expressions • Enumeration is term-directed • A function of AST nodes only • More enumerated attempts better messages • Called by Searcher when needed
Architecture – Ranker Defines the relative merit of successful fixes: • Search can produce too many results • Not all results are helpful to user • E.g. “Replace whole program with ”! • Use heuristics to filter and sort messages • “Smallest fixes are best” • “Currying a function is better than deleting it” • Simple heuristics seem sufficient
Preliminary results • ML prototype: on ocaml-3.08.4 • Mostly complete • C++ prototype: on Eclipse 3.2, CDT 3.1, gcc 3.4.4 • Active work • Ongoing analysis of ~2K ML files collected from students • Group messages as “good”, “misleading”, “bad” • Check if message precisely locates problem • Check if message approximates student’s actual fix • Results: • Very good precision on small test cases • Good precision on real, large problems • Most poor results stem from common cause
Errors may be widely separated Least common parent is too big Idea: ignore most code, focus on one error Triage:Trade “complete”for “small” Dealing with multiple errors
Conclusions • Searching for repairs yields intuitively helpful messages • SEMINAL decouples type-checker from error-message generator • Simpler TC architecture • Smaller trusted computing base • It’s a win-win scenario!