love-haskell / coercible-utils

Utility functions for Coercible types
https://hackage.haskell.org/package/coercible-utils
BSD 3-Clause "New" or "Revised" License
9 stars 3 forks source link

Clarify what's unused #13

Closed treeowl closed 5 years ago

treeowl commented 5 years ago

Following profunctors, make the types indicate what's unused.

chessai commented 5 years ago

Ah, neat trick. I didn't know about this. Waiting for travis, then I'll take another look.

chessai commented 5 years ago

@sjakobi what are your thoughts on this? I'm in favour. The only drawback might be that type errors are slightly more confusing, though it's unlikely that users are going to pass anything but a function in practise anyway. also, perhaps the use of this trick should be documented.

treeowl commented 5 years ago

I'm not so sure about the type variable name/presentation. I think we should use something infix to suggest a function arrow. Since we can't use actual operator characters in a type variable, maybe something like a `arr` b?

treeowl commented 5 years ago

Or maybe a `to` b?

sjakobi commented 5 years ago

Neat idea!

We have the following note in the haddocks:

All of the functions in this module take an argument that solely directs the type of the coercion. The value of this argument is ignored.

So how about something like

type Director a b = a -> b

We could then have haddocks on the type alias.

sjakobi commented 5 years ago

BTW @chessai, had you seen https://github.com/sjakobi/newtype-generics/pull/5? I sometimes feel bad about having this neat API just lying around. I suspect that it's actually better than what either coercible-utils or newtype-generics currently offer.

chessai commented 5 years ago

Neat idea!

We have the following note in the haddocks:

All of the functions in this module take an argument that solely directs the type of the coercion. The value of this argument is ignored.

So how about something like

type Director a b = a -> b

We could then have haddocks on the type alias.

I think I'd prefer something more like type Director p a b = p a b. That way they could pass anything of kind Type -> Type -> Type. But, perhaps this flexibility is unnecessary, and your type alias would be preferred, since the only goal here is clarity of consumption.

treeowl commented 5 years ago

I'm not a fan of this Director business, for two main reasons. One is that I'm opposed to type synonyms in most cases; they add mental indirection for nothing here. Also, I don't even see how that's supposed to be mnemonically helpful here. Much better, I think, to give the type variable a name that indicates what it's supposed to do.

treeowl commented 5 years ago

I think the right place to document what the to/arr/coerceTo type variables are about is the documentation at the top of the module.

treeowl commented 5 years ago

By the way... if you want to keep precisely the same API as before while still communicating the same thing, one option would be

class IsArrow p | -> p
instance IsArrow (->)
-- or
class IsArrow p where
  type Silly p -- banish overlapping instances
instance p ~ (->) => IsArrow p where
  type Silly (->) = ()
(#.) :: (Coercible b c, IsArrow p) => p b c -> (a -> b) -> a -> c
(#.) _ = coerce (#.) _ = coerce

This means the argument will be inferred as having a function type, but (#.) can't make use of that fact. I don't think this is really a good idea, but I figured I'd mention it...

chessai commented 5 years ago

I'm not a fan of this Director business, for two main reasons. One is that I'm opposed to type synonyms in most cases; they add mental indirection for nothing here. Also, I don't even see how that's supposed to be mnemonically helpful here. Much better, I think, to give the type variable a name that indicates what it's supposed to do.

I generally share this opinion. Type synonyms are generally distracting, I only like using them for quick hacking. Almost all usage of type synonyms are banned at work, except for the common transformers+Identity story.

Okay, I think we should just name p to something useful and make it infix.

treeowl commented 5 years ago

@chessai, for what it's worth, I have found more uses for type synonyms than that. Several higher-rank ones can be useful, like type f ~> g = forall x. f x -> g x, and stuff from lens (though that's always controversial). In heavily typish code they often show up with type families, such as

type family ReverseOnto acc xs where
  ReverseOnto acc '[] = acc
  ReverseOnto acc (x ': xs) = ReverseOnto (x ': acc) xs

type Reverse xs = ReverseOnto '[] xs

I recently found them useful for working around nasty limitations of the type role system, where sticking a newtype in how I'd prefer prevented necessary coercions (the whole mess got wrapped under a newtype before exposing it to the world anyway).

My final "approved by me!" use for type synonyms is as throw-away constraint synonyms appearing once as a superclass constraint, once as an instance constraint, and never again.

type CS a b c d = (Blah a, Boop a b, Bleep b c, Huh c d b, .......)
class CS a b c d => C a b c d
instance CS a b c d => C a b c d

Sorry for the irrelevant ramble; can't help myself.

chessai commented 5 years ago

it's ok; i enjoy your rambles. i can see uses for all of those.

sjakobi commented 5 years ago

Yeah, Director wasn't a good name.

I'd be fine with something like

a `to` b

or

coerce a b

Although I'd like to see the haddocks before we make the final decision.

treeowl commented 5 years ago

I changed the name of the type variable and updated the documentation.

sjakobi commented 5 years ago

Thank you @treeowl! :)