[Haskell-cafe] Language extension proposal: aspects
Dmitry Olshansky
olshanskydr at gmail.com
Sat May 6 19:37:22 UTC 2017
How does compiler can infer a type for "allEven = foldMap even" ?
Something like
allEven :: (Monoid (aspect Bool), Integral a) => [a] -> Bool ?
Should all Bool's in function body have the same aspects?
2017-05-06 19:42 GMT+03:00 MarLinn <monkleyon at gmail.com>:
> Hi people,
>
> in the last couple days this list has once again seen examples of how our
> class system is not perfect yet. Here are some of the problems we face:
>
> - Useful, but confusing instances like Foldable ((,) a)
> - Alternative possible instances like Alternative []
> - Orphan instances
> - Reliance on the order of arguments makes some instances impossible,
> for example Traversable (,a)
>
> How we usually resolve some of such issues is with newtype. Among the
> drawbacks are
>
> - This clutters code with artificial wrappers and unwrappers that have
> nothing to do with the task at hand
> - It implies two levels of hierarchy by marking the one instance
> without a newtype as special
> - Every type needs its own wrapper. E.g. a Foldable (,a) (if possible)
> would need a different wrapper from a Foldable (a,,c)
> - Definitions are scattered at unexpected places, like All and Any,
> partly to avoid orphan instances.
>
> After some thought I therefore propose a language extension I call
> "aspects". Keep in mind that this is a very rough draft just to gauge your
> reaction.
>
> The core change would be the introduction of a keyword "aspect" that
> would work in a comparable way to the keyword "module". In other words
> you could say
>
> aspect Data.Aspect.ChooseNonEmpty where
>
> import qualified Data.Set as Set
>
> instance Alternative [] where
> empty = []
> a <|> b = if null a then b else a
>
> instance Alternative Set.Set where …
> empty = Set.empty
> a <|> b = if null a then b else a
>
> Changes compared to a normal module would be:
>
> - An aspect can only contain instances
> - An aspect can import anything, but exports only instances
> - An aspect will never produce orphan instance warnings (duh.)
> - An aspect can be a file level definition, but it can also be
> contained in a module (we'll see why that is useful)
>
> You also wouldn't import an aspect like a normal module, but with a second
> syntax extension:
>
> import Data.List under (Data.Aspect.ChooseNonEmpty)
> import qualified Data.Set as Set hiding (Set)
> import qualified Data.Set under (Default, Data.Aspect.ChooseNonEmpty) as CNE (Set)
>
> So you could also import the same structure twice with different aspects
> under different names:
>
> import Data.Bool
> import qualified Data.Bool under (Data.Aspect.All) as All (Bool)
> import qualified Data.Bool under (Data.Aspect.Any) as Any (Bool)
>
> Now, because of the first import, you could use the boolean functions
> normally, and even use the normal Bool type in signatures. And because of
> the qualified imports if you want to use one of the Monoid instances, all
> you would have to do is change Bool in the type signature to Any.Bool or
> All.Bool respectively, like so:
>
> allEven :: (Integral a) => [a] -> Bool
> allEven = foldMap even -- error: Could not deduce (Monoid Bool)…
>
> -- old way
> allEven :: (Integral a) => [a] -> Bool
> allEven = getAll . foldMap (All . even)
>
> -- new way
> allEven :: (Integral a) => [a] -> All.Bool -- qualified name adds the monoidal aspect (and possibly others)
> allEven = foldMap even -- works
>
> In other words, aspects would only be used for instance lookups. That is
> also why you could state several aspects at once when importing. Conflicts
> would be solved as usual: All is well until you try to use class functions
> that create an ambiguity.
>
> I imagine a few special rules to make backwards compatibility easier. Most
> importantly, you could define default aspects in several ways:
>
> - aspect Default where … -- reserved aspect name
> - default aspect ChooseNonEmpty where … -- re-used reserved keyword,
> but also gives a name to the aspect
> - default aspect where … -- short form for default
> aspect Default where …
> - An instance defined in a module outside of an aspect would
> automatically be in the Default aspect. In other words the definition
> can be seen as a short form of a local extension to the aspect. That's also
> why aspects would be allowed to be part of a module.
>
> If you don't specify an aspect while importing, it would be imported under
> the Default aspect. To hide the Default aspect, just don't add it to the
> aspect list when importing.
>
> Other random thoughts about this:
>
> - An aspect doesn't need to be complete. E.g. I imagine an aspect that
> only defines Alternative.empty, with several other aspects relying on
> that by importing this incomplete aspect. OO programmers might call them
> abstract aspects. This might possibly help resolve some disputes about the
> "perfect" hierarchy.
> - If aspects can be part of a module, can aspects also be part of an
> aspect? Maybe, but I haven't made a cost-benefit analysis yet.
> - Aspects seem to form a level of container between definitions and
> modules. Maybe there should also be a new container type (or several) for
> the other parts of code? Say, a container that can contain everything
> *but* instances.
> - There could be an extension to the export syntax to choose which
> aspects to export. I just don't see the usefulness right now.
> - There could also be special syntax like import * under
> (Default,SpecialAspect) as a short form to add some aspects to every
> imported module.
> - The Default aspect is obviously extensible. I consider that a
> universally useful, if not essential feature. On the other hand in this
> proposal aspects use the module name space – which means such extensions
> would only be possible on a package level or by using several code folder
> roots. I'm not sure I'm happy with that.
> - The proposal doesn't solve the issue that instances rely the order
> of arguments. But an introduction of such new syntax might be a good time
> to introduce two extensions at once. I imagine something like instance
> Foldable (,) _ a where…
>
> The biggest drawbacks from this idea that I can see right now are:
>
> - The language extension might be infectious: once one library in a
> project uses it, many parts of the project might need it. This is different
> from most extensions that stay rather local.
> - Such a change might necessitate huge amounts of cpp.
> - Because aspects would be extensible and would have a global name
> space, care would have to be taken to not create a mess.
>
> So… feel free to bikeshed and more importantly criticize! What am I
> overlooking? Would you consider such an idea worthwhile? Happy to hear your
> thoughts.
>
> Cheers,
> MarLinn
>
> _______________________________________________
> Haskell-Cafe mailing list
> To (un)subscribe, modify options or view archives go to:
> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
> Only members subscribed via the mailman list are allowed to post.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20170506/ac4f1062/attachment.html>
More information about the Haskell-Cafe
mailing list