[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