Templates in FPL?
Marcin 'Qrczak' Kowalczyk
qrczak@knm.org.pl
18 May 2001 19:11:21 GMT
Fri, 18 May 2001 11:25:14 +0200, Jerzy Karczmarczuk <karczma@info.unicaen.fr> pisze:
> Always when somebody mentions templates in presence of a True
> Functionalist Sectarian, the reaction is "What!? Abomination!!".
They aren't that wrong, but they have some problems:
* It's not specified what interface they require from types to be
instantiated to.
* The whole implementation must be present in a public header file.
Well, there is the 'export' keyword, but no compiler implements it.
It follows that all things used by implementation of a template must
be included too. And compilation time of a large project is larger
than necessary.
* They introduce very complex rules about name lookup (environment
in which a specialized template is compiled is a weird mix of
environments of the point of definition and point of usage of the
template), deduction which overloading to use (with choosing the
candidate according to partial ordering of better fitting), deduction
of template types and what partial specialization to use, complicated
by the fact that there are complex rules of implicit conversions.
Templates don't obey a Haskell's rule that adding code doesn't change
the meaning of previously valid code and can at most introduce an
ambiguity which is flagged as error.
* Types can't be deduced from the context of usage. For example
handling the empty set or monadic 'return' would require either
spelling the type at each use or introducing another type and
deferring deduction of the real type until an operation is performed
with a value of a known type.
* Since they are similar to macros, they tend to give horrible error
messages which mentions things after expansion. Here is what I
once got:
mico-c++ -Wall -I. -c serwer.cc -o serwer.o
serwer.cc: In method `void Firma_impl::usun(const char *)':
serwer.cc:92: `struct __rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,const pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,const pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> *>' has no member named `second'
serwer.cc:93: no matching function for call to `map<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *,less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >,__default_alloc_template<true,0> >::erase (__rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,const pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,const pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> *> &)'
/usr/include/g++/stl_map.h:156: candidates are: map<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *,less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >,__default_alloc_template<true,0> >::erase<string, Katalog *, less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >, alloc>(__rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> *>)
/usr/include/g++/stl_map.h:157: map<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *,less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >,__default_alloc_template<true,0> >::erase<string, Katalog *, less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >, alloc>(const string &)
/usr/include/g++/stl_map.h:158: map<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *,less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >,__default_alloc_template<true,0> >::erase<string, Katalog *, less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >, alloc>(__rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> *>, __rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,pair<const basic_string<char,string_char_traits<char>,
__default_alloc_template<true,0> >,Katalog *> *>)
make: *** [serwer.o] Error 1
They also have some advantages over mechanisms available in Haskell:
* A templatized class can contain types, values with arbitrary types,
other templates etc. so it's easy to have parametrization of all
things by types. Haskell requires fundeps, dummy arguments and
local quantifiers to handle these things - sometimes it's not nice.
* A template can be parametrized by values of primitive types (by
expressions using builtin operations which can be evaluated at
compile time). Yes, it's ugly that types and operations don't have
equal rights, but it's sometimes useful.
* A template is in practice guaranteed to be specialized to used types,
so they don't have a performance overheads over non-templatized
variants. My not-quite-done-right attempt at inlining dictionary
functions in ghc is a step in this direction, as is the proposal of
export-unfolding-and-specialize-but-not-necessarily-inline. I hope
that SimonPJ will sort this out.
--
__("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/
\__/
^^ SYGNATURA ZASTĘPCZA
QRCZAK