[Haskell-cafe] Safe Haskell at the export symbol granularity?
illissius at gmail.com
Sun Jun 17 22:30:50 CEST 2012
Thanks for the long answer!
It just occured to me that Data.Typeable is in basically the same
situation. Data.Typeable is a Trustworthy module with a type class,
and with functions built on that class, which are safe if they are
used with types for which the instances of the class are "proper".
Using only safe modules and imports, it is not possible to subvert
that safety: instances can only be derived, and derived instances are
guaranteed to be "proper". But if someone makes a Trustworthy module
with an improper manual Typeable instance and you import it, suddenly
the functions in Data.Typeable can be unsafe, even though nothing in
that module has changed.
The only difference in my situation is that there's no
guaranteed-to-be-safe deriving mechanism, it all rests on whether the
Trustworthy modules importing my Unsafe module to write their
instances are behaving themselves.
I think one way to resolve this might be to say that Safe Haskell's
mission is to help prevent bad things from happening in a global
sense, but not necessarily to delineate which functions can or cannot
be causing the bad things once unsafeness has crept into the program.
Trustworthy modules are inherently risky: they declare of themselves
that they're trustworthy, but really, they could do anything. The
burden is on the administrator to decide what they actually trust.
What Safe Haskell says is that if the administrator does this properly
and only trusts packages/modules which are actually safe, then
programs will not behave unsafely. But if they make a bad decision and
trust an unsafe module, then whether it does the bad things directly,
or indirectly by breaking the invariants another module depends on,
doesn't make much of a practical difference. You've lost either way.
On Sat, Jun 2, 2012 at 2:28 AM, David Terei <dave.terei at gmail.com> wrote:
> So this is a good question, sorry for the late reply. It's tricky as
> the way typeclasses are imported and exported in Haskell is confusing.
> Basically, instances are hard to control access to as they aren't part
> of import or export statements. Importing a module that defines an
> instances gives you those instances. This works transitively so you
> have access to all instances defined below you in the dependency
> Controlling access to a typeclass function is easy though, it works
> just like a normal function. So in your example, the Safe module
> wouldn't necessarily become unsafe but there is some unsatisfactory
> - untrusted code still couldn't access the type class as the
> functions for it aren't exported.
> - the derived functions may or may not be safe anymore depending on
> - If the derived functions don't have any polymorphism that would
> allow consumers of the functions to choose what underlying typeclass
> is used, then the module is still safe.
> - If they do, then yes untrusted code could choose what types to
> use to cause the unsafe instance to be used, thus making the derived
> functions unsafe. (This assumes the untrusted code has access to the
> unsafe instance but as I said, this is hard to reason about since
> instances are somewhat global).
> there are solutions to this problem but its a tricky situation with
> the solutions really being to be careful... I don't know how we could
> do better. Tracking safety at the symbol level doesn't seem like it
> would change this situation. Basically you want closed type classes or
> a way to control what instances can be used (maybe by simply making
> instances part of import/export lists) both of which are big changes
> to Haskell.
> I wrote some example code and a note about this stuff:
> On 18 May 2012 06:58, Gábor Lehel <illissius at gmail.com> wrote:
>> I have a related-seeming question:
>> Say I have a type class with methods, and some functions implemented
>> on top of it. The class methods are inherently unsafe. Instances of
>> the class are supposed to satisfy some conditions, and if those
>> conditions are met, the functions built on top are safe.
>> So say I put the class in an Unsafe module, and re-export the class
>> without its methods along with the derived functions in a Safe module.
>> For anything unsafe to happen, the Unsafe module has to be imported
>> somewhere. But if someone imports it and implements a bad instance,
>> the Safe module *also* becomes potentially unsafe! What's the
>> recommended practice here?
>> (I can't really tell if this is the same question as originally posed
>> by Ryan, but I think it's not.)
>> On Thu, May 17, 2012 at 4:53 PM, Ryan Newton <rrnewton at gmail.com> wrote:
>>> Good point, Antoine!
>>> I think that does the trick.
>>> On Thu, May 17, 2012 at 10:48 AM, Antoine Latter <aslatter at gmail.com> wrote:
>>>> On Thu, May 17, 2012 at 8:50 AM, Ryan Newton <rrnewton at gmail.com> wrote:
>>>> > Thanks David.
>>>> > I'm glad to see it was discussed in the wiki. (Btw, my 2 cents is that
>>>> > I
>>>> > like the comment pragmas more than new keywords.)
>>>> > The issue that I think doesn't make it into the wiki is of splitting,
>>>> > not
>>>> > modules, but type-classes. That's where I think it becomes a more
>>>> > serious
>>>> > issue.
>>>> > Do you think a symbol-level Safe Haskell would be able to distinguish
>>>> > one
>>>> > method of a type class as unsafe, while the others are safe?
>>>> You can still do this at the module level, with the down-side of
>>>> potentially not being able to implement a class with the safe version:
>>>> > module Unsafe where
>>>> > class MyClass a where
>>>> > safeOp :: a -> Int -> IO ()
>>>> > unsafeOp :: a -> Int -> IO ()
>>>> > instance MyClass A where ...
>>>> > module Safe
>>>> > (MyClass(safeOp))
>>>> > where
>>>> > import Unsafe
>>>> I think this works.
>>> Libraries mailing list
>>> Libraries at haskell.org
>> Work is punishment for failing to procrastinate effectively.
Work is punishment for failing to procrastinate effectively.
More information about the Haskell-Cafe