Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Jan 02 23:58
    @SethTisue banned @fakubishes:nerdsin.space
  • Dec 15 2021 05:01
    som-snytt commented #12516
  • Dec 15 2021 04:38
    SethTisue edited #1312
  • Dec 15 2021 04:38
    SethTisue opened #2273
  • Dec 15 2021 04:31
    jackkoenig opened #12516
  • Dec 15 2021 04:29
    SethTisue edited #1312
  • Dec 15 2021 04:28
    SethTisue edited #1312
  • Dec 15 2021 04:27
    SethTisue labeled #9831
  • Dec 15 2021 04:27
    scala-jenkins milestoned #9831
  • Dec 15 2021 04:27
    SethTisue labeled #9831
  • Dec 15 2021 04:27
    SethTisue opened #9831
  • Dec 15 2021 03:35
    som-snytt commented #11339
  • Dec 15 2021 03:27
    som-snytt labeled #12494
  • Dec 15 2021 03:07
    SethTisue edited #1312
  • Dec 15 2021 03:07
    SethTisue edited #1312
  • Dec 15 2021 03:05
    SethTisue edited #1312
  • Dec 15 2021 03:05
    SethTisue edited #1312
  • Dec 15 2021 03:05
    SethTisue edited #1312
  • Dec 15 2021 02:58
    SethTisue edited #1312
  • Dec 15 2021 02:58
    SethTisue synchronize #1312
RoelofWobben
@RoelofWobben
the first three are explained in the cats book
the syntax is different and that is the most difficult to pick up
@GrafBlutwurst
RoelofWobben
@RoelofWobben
time to sleep here , see you all tomorrow
Jason Pickens
@steinybot

I’m having some problems with path dependent types. Not sure if what I am trying to do makes sense. Anyway here is an example:

sealed trait Foo {
  type F
}

object Bar extends Foo {
  type F = Array[Byte]
}

trait Baz[B] {
  def b(b: B): String
}

object Baz {
  implicit val bazByteArray: Baz[Array[Byte]] = (b: Array[Byte]) => new String(b)
}

def f(a: Foo): Baz[a.F] = {
  val baz = a match {
    case bar@Bar => g(bar)
  }
  baz
} // Expression of type Baz[(a.type with Bar.type)#F] doesn't conform to Baz[a.F]

def g(a: Foo)(implicit baz: Baz[a.F]): Baz[a.F] = {
  baz
}

val x1: Bar.type = Bar
val y1: Baz[x1.F] = f(x1)

val x2: Foo = Bar
val y2: Baz[x2.F] = f(x2) // Expression of type Baz[Foo#F] doesn't conform to expected type Baz[x2.F]

What I don’t get is why g compiles but f doesn’t. Are the types not the same?

zetashift
@zetashift
Just a question out of curiosity, making a function a first class value you need to add an apply method(function?), why is it named apply?
Mathieu Prevel
@mprevel
object Test {
  def apply() = doSomething()
}
can be called Test()
this is a special case
Ichoran
@Ichoran
Ugh, this expression stuff is so clunky in this FP example.
There are perfectly reasonable OO ways to make the whole thing tidy, e.g.
trait Expr { def eval: Either[String, Double] }
sealed trait BinOp extends Expr {
  def a: Expr
  def b: Expr
  def op: (Double, Double) => Either[String, Double]

  protected def safe(f: (Double, Double) => Double): (Double, Double) => Either[String, Double] = 
    (xa, xb) => Right(f(xa, xb))

  def eval = a match {
    case Left(s) => Left(s)
    case Right(xa) => b match {
      case Left(s) => Left(s)
      case Right(xb) => op(xa, xb)
  }
}
final case class Add(a: Expr, b: Expr) extends BinOp { val op = safe(_ + _) }
final case class Sub(a: Expr, b: Expr) extends BinOp { val op = safe(_ - _) }
final case class Mul(a: Expr, b: Expr) extends BinOp { val op = safe(_ * _) }
final case class Div(a: Expr, b: Expr) extends BinOp {
  val op = (xa, xb) => if (xb == 0) Left("Division by 0") else Right(xa / xb)
}
Seth Tisue
@SethTisue
@zetashift because that’s what you’re doing, applying the function. it isn’t the only possible word that could have been chosen, for example JavaScript has apply but it also has call, Lisp has funcall, and so forth. Java also calls it apply
Fabio Labella
@SystemFw
the two solutions aren't equivalent with respect to extensibility @Ichoran . Expression problem and that
ime the OO one is the side of it you want less often
(final tagless can solve both sides, but that's a separate discussion)
Ichoran
@Ichoran
I don't know, I've written half a dozen expression evaluators and never once wanted to make the expressions extensible outside of the code that defines the operations in a way that isn't trivially handled by inheritance.
Fabio Labella
@SystemFw
you never wish to add a pretty printer and an evaluator?
adding ops is fairly common
adding nodes, less so
Ichoran
@Ichoran
Not outside of the evaluator, no.
I can of course envision cases where it's helpful, but I haven't run into them.
Normally, I do it so that end-users won't have to bother thinking about what makes for a correct expression or how to pretty-print things; that's the point of me writing it at all.
Hopefully the FP example eventually gets to the point where it is helpful instead of just awkward?
Fabio Labella
@SystemFw

Normally, I do it so that end-users won't have to bother thinking about what makes for a correct expression or how to pretty-print things; that's the point of me writing it at all.

I don't understand this

as in , can't parse it
Ichoran
@Ichoran
So, I generally write expression trees like this when I want to offer a DSL as part of the API. The reason I do this is so I can think through the nonobvious stuff like what operations are valid and handle all the corner cases for the consumer of the DSL.
Abdhesh Kumar
@abdheshkumar
Just use flatMap on Future[Option[Double]]
Ichoran
@Ichoran
Extensibility implies that they get access to all the corners upon which they can poke themselves.
Avremel Kaminetzky
@avremel
@abdheshkumar thanks,
Ichoran
@Ichoran
Does that make more sense, @SystemFw ?
Fabio Labella
@SystemFw
well, if you're making a DSL, being able to interpret it seems reasonable
but the thing is
if you never need to extend it in any direction
the two approaches are exactly equivalent
except with the OO one the code logic is more scattered
rather than in one place like pattern matching
Abdhesh Kumar
@abdheshkumar
val r: Future[Option[Double]] = ???
r.flatMap{
case Some(v)=> Future.successful(v)
case None=> Future.failed(new Exception)}
Ichoran
@Ichoran
The pattern matching has a ton of repetition.
Fabio Labella
@SystemFw
where?
Ichoran
@Ichoran
Well, I guess I haven't seen what the gold-standard answer is supposed to be.
Fabio Labella
@SystemFw
I think the repetition there is mostly due to not being able to work with Either idiomatically because the appropriate combinators haven't been introduced yet
so a lot of matching on Left and Right
Ichoran
@Ichoran
Even then it's more machinery to get back to the same cleanliness of specifying operations.
Fabio Labella
@SystemFw
transforming things through pattern matching (complex trees) can indeed be tedious (that's where recursion schemes come into play), but I think that when you reach that point, the OO approach is hopeless anyway
@Ichoran I guess it depends on what you mean by cleanliness. For me being able to see all the logic for a given interpreter in one place is cleaner than it being scattered
Ichoran
@Ichoran
Yeah, OO is not well-suited to expression-simplification because it has very local perspective.
I find it's easier to know "everything about Add" in one place, but I guess tastes may differ there.
Fabio Labella
@SystemFw
sure, and again they extend in different directions
if you want to add Mult, pattern matching breaks everywhere
if you want to add prettyPrint, overriding breaks everywhere
final tagless handles both :)