0

Aloha! :)

Could please somebody point me to a useful scala/cats tutorial? I’m struggling with making a class a functor for the last few days and I’m about to punch a hole in my display. All the documentation I’ve found up until now was not of help for me.

Maybe I should give Eta a try... =D

Here is the class I would like to turn into a functor. Besides 'show' doesn't behave the way I expected also.

package org.hudelundpfusch.utilites.decisions.data

import cats.{Functor, Show}
import cats.kernel.Eq
import cats.syntax.functor._
import cats.syntax.show._

import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._

case class Fact[T <: Any] (name: String, value: T) (implicit private val paramTypeTagT: WeakTypeTag[T])
  extends Equals {

  val paramType: universe.Type = paramTypeTagT.tpe

  val paramTypeClass: Option[Class[_ <: T]] = if (value != null) {
    Some(value.getClass)
  } else {
    None
  }

  def map[A, B](fa: Fact[A])(f: A => B): Fact[B] = Fact[B](fa.name, f(fa.value))

  override def canEqual(other: Any): Boolean = other.isInstanceOf[Fact[_]]

  override def equals(other: Any): Boolean = other match {
    case that: Fact[_] =>
      (that canEqual this) &&
        name == that.name
        paramType == that.paramType &&
        paramTypeClass == that.paramTypeClass &&
        value == that.value
    case _ => false
  }

  override def hashCode(): Int = {
    val state = Seq(name, paramType, paramTypeClass, value)
    state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
  }

  override def toString = s"Fact(name=${name}, paramType=$paramType, paramTypeClass=$paramTypeClass, value=$value)"

}

case object Fact extends Show[Fact[_]] {

  override def show(t: Fact[_]): String = t.toString

}

Thanks in advance

Have a nice day

Alex

Update:

package org.hudelundpfusch.utilites.decisions.data

import cats.{Functor, Show}
import cats.kernel.Eq
import cats.syntax.functor._
import cats.syntax.show._

import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._

case class Fact[T <: Any] (name: String, value: T) (implicit private val paramTypeTagT: WeakTypeTag[T])
  extends Functor[Fact]
    with Equals {

  val paramType: universe.Type = paramTypeTagT.tpe

  val paramTypeClass: Option[Class[_ <: T]] = if (value != null) {
    Some(value.getClass)
  } else {
    None
  }

  def map[A, B](fa: Fact[A])(f: A => B): Fact[B] = Fact[B](fa.name, f(fa.value))

  override def canEqual(other: Any): Boolean = other.isInstanceOf[Fact[_]]

  override def equals(other: Any): Boolean = other match {
    case that: Fact[_] =>
      (that canEqual this) &&
        name == that.name
        paramType == that.paramType &&
        paramTypeClass == that.paramTypeClass &&
        value == that.value
    case _ => false
  }

  override def hashCode(): Int = {
    val state = Seq(name, paramType, paramTypeClass, value)
    state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
  }

  override def toString = s"Fact(name=${name}, paramType=$paramType, paramTypeClass=$paramTypeClass, value=$value)"

}

Okay, I tried this now:

object Fact {
  implicit val factFunctor: Functor[Fact] = new Functor[Fact] {
    override def map[A, B](fa: Fact[A])(f: A => B): Fact[B] = Fact[B](fa.name, f(fa.value))
  }

  implicit def factShow[T]: Show[Fact[T]] = new Show[Fact[T]] {
    override def show(t: Fact[T]): String = this.toString
  }
}

Unfortunately the call to the mapping function looks a bit cumbersome:

package org.hudelundpfusch.utilites.decisions.data

object Fuddel {
  def main(args: Array[String]): Unit = {
    val fact1: Fact[Int] = Fact("Fact-1", 23)
    val fact2 = Fact.factFunctor.map(fact1){x: Int => x * 2}

    println(s"$fact2")
  }
}
  • 1
    It seems to compile just fine. What's your question exactly? – Andrey Tyukin Apr 06 '19 at 12:36
  • 1
    Hello Andrey Thanks for the reply. I forgot to mention that I wanted to let this class extend cat's Functor and to put the neccessary mapping functions in a companion object. I expected that I would be able to call the mapping functions with infix annotation like so: fact: Fact[Int] = someFact map someFunction Alas, it doesn't work... Maybe I'm too dumb... =( Best regards Alex – Alexander Schell Apr 06 '19 at 12:46
  • 1
    Well... If you *wanted* to extend Functor, that's *not a problem*. If you actually *attempted* to implement Functor, and it didn't work, *then* it would be a problem. Currently, I don't see any mentions of `Functor` except in the one import clause. Please provide a [mcve] that demonstrates what the actual problem is. – Andrey Tyukin Apr 06 '19 at 12:50
  • 2
    Just updated my question... Maybe it's e little bit more clear now... – Alexander Schell Apr 06 '19 at 12:59
  • 1
    In this case, it seems that Dmytro Mitin guessed your problem correctly. – Andrey Tyukin Apr 06 '19 at 12:59

1 Answers1

6

With Cats you do not extend Show, Functor traits (OOP way), you create implicit instances of Show, Functor (FP way).

http://eed3si9n.com/herding-cats/Functor.html

https://typelevel.org/cats/typeclasses/functor.html

implicit def factShow[T]: Show[Fact[T]] = new Show[Fact[T]] {
  override def show(t: Fact[T]): String = ???
}

//  implicit object factFunctor extends Functor[Fact] {
//    override def map[A, B](fa: Fact[A])(f: A => B): Fact[B] = ???
//  }

implicit val factFunctor: Functor[Fact] = new Functor[Fact] {
  override def map[A, B](fa: Fact[A])(f: A => B): Fact[B] = ???
}

Show[Fact[Int]].show(Fact("a", 1))
Functor[Fact].map(Fact("a", 1))(_ + 1)

import cats.syntax.show._
Fact("a", 1).show

import cats.syntax.functor._
Fact("a", 1).map(_ + 1)

Just in case, Fact[_] in your case object Fact extends Show[Fact[_]] is existential type, and Fact in my implicit object factFunctor extends Functor[Fact] is higher-kinded type.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • @AndreyTyukin I don't think so. :-P – Dmytro Mitin Apr 06 '19 at 12:52
  • Ok... Then I have to admit that I have no idea what's going on here. There is neither an attempt to implement a `Functor`, nor an attempt to use `map` anywhere, just a wall of some compilable code. I don't see how this constitutes a valid question. – Andrey Tyukin Apr 06 '19 at 12:58
  • @AndreyTyukin You're too fast ;) – Dmytro Mitin Apr 06 '19 at 12:59
  • 1
    The question has been updated, it looks as if it's asking exactly what you answered. Causality? Happens-before relations? No, never heard of that... xD – Andrey Tyukin Apr 06 '19 at 13:02
  • I thought you have to extend these traits to make the compiler know that your class is a member of the typeclass... Fine, wrong approach. But how do I use these implicit objects? I thought an infix call to map should work like fmap in haskell. – Alexander Schell Apr 06 '19 at 13:05
  • @AlexanderSchell `implicit object factFunctor extends Functor[Fact]...` is like `instance Functor Fact where ...`. `import cats.syntax.functor._` `fact.map(f)` is like `fmap f fact`. – Dmytro Mitin Apr 06 '19 at 13:08
  • 2
    Two things to ads here. 1. Do not use **implicit objects**, they sometimes break implicit resolution, always use an **implicit val** with an explicit _type_ signature instead (`implicit val factFuntor: Functor[Fact] = new Functor[Fact] { ... }`). - 2. Just as a clarification, this is not a cats thing, it is the essence of the _typeclass_ pattern. Your class is not longer an `F`. But, it does has an instance of `F`. The trick is the differentiation of _is a_ VS _has a_. – Luis Miguel Mejía Suárez Apr 06 '19 at 13:15
  • Just updated my question. I have the feeling that I'm missing something basic and important... =/ – Alexander Schell Apr 06 '19 at 13:33
  • 2
    @AlexanderSchell As already mentioned above: you're missing the `import cats.syntax.functor._`. Once imported, you should be able to write `fact1.map(_ * 2)`. – Andrey Tyukin Apr 06 '19 at 13:34
  • 2
    @AlexanderSchell Huh, what? No. Please, if this solved your problem, just mark *the answer* as the accepted *answer*. If you think that something from the comment section should be copied into the answer, then please tell us, we can edit anything. Or suggest an edit yourself (I don't know, maybe this doesn't work below certain threshold). – Andrey Tyukin Apr 06 '19 at 13:47
  • Thanks to all of you! :) You helped my display to survive another day... – Alexander Schell Apr 06 '19 at 13:49
  • @LuisMiguelMejíaSuárez Unfortunately your advice #1 not always works. Sometimes implicit val/def breaks implicit resolution while implicit object resolves properly. And sometimes you can't annotate implicit val/def with explicit type because in Scala there are types that can't be expressed in source code. – Dmytro Mitin Apr 06 '19 at 14:42
  • @DmytroMitin I would be very interested in seeing an example of both: A case where **implicit object** works and **implicit val** don't. And, of a _type_ that can not be expressed in source code, especially related with implicits. I had never seen such thing. - _Not trying to sound pedantic or arrogant here, I am truly curios :)_ – Luis Miguel Mejía Suárez Apr 06 '19 at 16:12
  • 1
    @LuisMiguelMejíaSuárez Here is example where implicit object works and implicit val doesn't https://gist.github.com/DmytroMitin/ed22c9a84d5c89a46148a073b92052da And here is example where you can't annotate implicit `m` with any type so that the code compiles https://gist.github.com/DmytroMitin/0ef4c93b9a58a1f2e1d92f71fbdbfb19 – Dmytro Mitin Apr 06 '19 at 16:25
  • 1
    @LuisMiguelMejíaSuárez `MyTrait` and `shapeless.Poly1` are not type classes. But we can add type parameter and they will become type classes. – Dmytro Mitin Apr 06 '19 at 16:33
  • 1
    @LuisMiguelMejíaSuárez these are types present in scalac https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/internal/Types.scala#L31-L84 Not all of them can be created in source code. – Dmytro Mitin Apr 06 '19 at 16:46
  • @DmytroMitin I see that in the first example, the implicit val does not work, because you loose the necessity of having a subtype of `Poly1` with a _self.type_ which is probably required for the things shapeless do, but I can not see how the compiler will be able to infer which is the one you want if there more than one, without being explicit and in that case, why not simply passing the object and leaving the `poly.Case[A, ...]` as the only implicit? _(disclaimer, I know nothing about shapeless and path-dependent types - both are next in my list of things to learn)_ – Luis Miguel Mejía Suárez Apr 06 '19 at 17:22
  • @DmytroMitin On your second example I see the problem is that one can not say that a val in a structural type has to be implicit _(did know about that)_. But, first an implicit dependent type from another implicit seems to me like an over complicated example. Second, again, how are you going to ask for it if you can not be explicit? Or how will the compiler find the right one if there are more than one? – Luis Miguel Mejía Suárez Apr 06 '19 at 17:28
  • @DmytroMitin I can agree that on some corner, very specialized, cases those two examples makes sense. And that saying _never_ use **implicit object** is bad _(as any use of never and always)_. I also remember Rob Norris saying something like that on gitter this week. But, for majority of use cases, especially the common ones, I still believe **implicit vals with _explicit_ type signatures** is the best. Anyways, it was fun reading your examples ty. IMHO, we should just end the discussion here to avoid more comments :) – Luis Miguel Mejía Suárez Apr 06 '19 at 17:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/191377/discussion-between-dmytro-mitin-and-luis-miguel-mejia-suarez). – Dmytro Mitin Apr 06 '19 at 19:58