and it would render a great deal of existing code uncompilable.
def map[B](f: A => B): List[B]
How to annotate map with @throws? If map does not get a @throws
annotation itself then presumably you cannot pass it any function that
has a @throws. That would introduce cumbersome restrictions and
distinctions for the ways in which map can be used. Things would be
better if we could state somehow that map throws all exceptions that
its function argument throws. There are some effect systems that can
express this, but so far every notation I have seen is too heavy.
Lukas Rytz is doing some research on lightweight effect systems that
could be used to express the type of map and other common functions in
a concise and precise way. It's research, so it's at present unclear
to what degree we will succeed and how much of that can be put into
Scala. Ideally, we'll be able to add it at some point as an optional
type system. But it's much too early to make concrete predictions.
Cheers
-- Martin
When you really need this use Either and a decent error type.
Among the virtues of switching to Google Groups is the ability to link
to individual messages:
http://groups.google.com/group/scala-debate/msg/99369ca6c4959894
> On Sat, Feb 5, 2011 at 10:39, martin odersky wrote:
> > ...
Randall Schulz
On 05/02/11 22:39, martin odersky wrote:
> The problem with checked exceptions is best demonstrated by the
> map method on lists:
>
> def map[B](f: A => B): List[B]
>
> How to annotate map with @throws?
Quite easy really.
First, let's generalise the signature:
F[A] => (A => B) => F[B]
Now let's abstract it to a trait:
trait Functor[F[_]] {
def fmap[A, B](a: F[A], f: A => B): F[B]
}
Now let's recognise that this "throws" is just another data type (like
Either):
sealed trait Throws[A]
case class Thrown[A](t: Throwable) extends Throws[A]
case class NoThrow[A](value: A) extends Throws[A]
Now observe that Throws satisfies the Functor trait:
def ThrowsFunctor: Functor[Throws] = new Functor[Throws] {
def fmap[A, B](a: Throws[A], f: A => B) = a match {
case Thrown(t) => Thrown(t)
case NoThrow(a) => NoThrow(f(a))
}
}
Now the question is, how do we map to a value T, that might throw, as
in Throws[T]?
We use the Throws transformer:
case class ThrowsT[F[_], A](th: F[Throws[A]])
Now observe that ThrowsT[F, _] satisfies the Functor trait, so long as
F satisfies the Functor trait. That is to say, ThrowsT type
constructor, partially-applied gives us a functor. This is going to
require some scala gymnastics, but it's perfectly viable:
def ThrowsTFunctor[F[_]](ftr: Functor[F]): Functor[({type
?[?]=ThrowsT[F, ?]})#?] =
new Functor[({type ?[?]=ThrowsT[F, ?]})#?] {
def fmap[A, B](a: ThrowsT[F, A], f: A => B) = a match {
case ThrowsT(th) =>
ThrowsT(ftr.fmap(th, (a: Throws[A]) => ThrowsFunctor.fmap(a,
f)))
}
}
By adding some convenience functions (such as A => ThrowsT[F, A]) and
abstractions, this ThrowsT data type allows us to treat regular values
as well as those that may throw in their computation all within the
same abstraction ("the ability to map").
- --
Tony Morris
http://tmorris.net/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iEYEARECAAYFAk1PC84ACgkQmnpgrYe6r62qCgCgz1/icLutT9RyiR7U6xgeXfNZ
X4gAnjR8rDNljqndoRbT5JnvgIpET3B2
=Vz01
-----END PGP SIGNATURE-----