Skip to content
This repository has been archived by the owner on Feb 8, 2022. It is now read-only.

Semigroup/Monoid.combineN: Express partiality with values not exceptions #84

Open
jbgi opened this issue Sep 24, 2015 · 6 comments
Open

Comments

@jbgi
Copy link

jbgi commented Sep 24, 2015

After reading http://plastic-idolatry.com/erik/sw2015.pdf it looks like
Semigroup.combineN and Monoid.combineN currently only meet goal number 7 (Pragmatic) but not goal number 2 (Safe: Express partiality with values not exceptions).

  /**
   * Return `a` combined with itself `n` times.
   */
  def combineN(a: A, n: Int): A =
    if (n <= 0) throw new IllegalArgumentException("Repeated combining for semigroups must have n > 0")
    else repeatedCombineN(a, n)
@johnynek
Copy link
Contributor

yes. That's right. Since returning Option[A] would require boxing, that was done. Shame we don't have some kind of non-negative integer type.

@non might want to comment. That method does not come up much at all as far as I know (I don't think I've ever used it in Twitter's code in Algebird, though we have an equivalent version).

We could put combineN in Group and combineNOption in Semigroup.

@non
Copy link
Contributor

non commented Sep 24, 2015

It's true.

Other places you'll notice this problem are Field[A]#div (which returns A not Option[A]) and the pow operator as well (and sqrt/nroot in Spire).

@non
Copy link
Contributor

non commented Sep 24, 2015

I think the current API is done since it is total for Group. In this case some kind of combineNOption makes sense -- but I think that the partiality guidelines will probably have to give in the face of e.g. division.

What I really want is a version which requires a literal Int whose sign can be checked at compile-time. (We could then use the thing to make safer versions of e.g. pow.)

@tixxit
Copy link
Contributor

tixxit commented Sep 24, 2015

@non See typelevel/spire#438 - we can get unboxed (value class) + optionally checked at compile time for constants.

Specifically: https://github.com/kevinmeredith/spire/blob/jetdim_compile_time_check/macros/src/main/scala/spire/macros/PositiveInt.scala

(It's a case class there, but a value class may make more sense)

@johnynek
Copy link
Contributor

PositiveInt is cool, maybe we should have something like that (also non-negative int might be good).

As an aside, we are likely to have partiality with div as Erik mentioned (and probably others). I wonder if we could create an annotation for such methods which might make it easier to test (no function should throw no matter what the inputs unless it has such an annotation).

You could imagine a macro that would prevent you from calling such methods inside the block (like a safe region), and we could add the total versions (which just lift the partiality into the return type).

It would be cool to have some trick with a method with a certain annotation could only be called INSIDE a given macro, like Rust's unsafe block. Emulating that in scala with macros + annotations would be awesome. I don't really see how to do it though (maybe with annotation macros if they are still a thing. You could name mangle the method with the @unsafe annotation and then remangle it inside the unsafe { } block. An alternate trick might be to abuse the package private somehow to do it, but not obvious to me how.

@non
Copy link
Contributor

non commented Sep 24, 2015

The annotation is a great idea (regardless of what else we do).

I'm going to see if I can get Travis successfully building the branch @tixxit linked. Right now it doesn't look like PositiveInt is a value class but I bet we can fix that.

I think the longterm strategy for doing this might involve passing evidence that unsafe methods will be called safely, either via dependent types, type tagging, or something else. If we play our cards right with macro annotations we could signal which things could fail, and make the unsafe { ... } macro work a bit like the async { ... } macro (i.e. if the evidence was absent, it could synthesize the safety checks, and bail when any of them failed).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants