Read case class object from string in Scala (something like Haskell's “read” typeclass)

后端 未结 6 966
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-13 20:32

I\'d like to read a string as an instance of a case class. For example, if the function were named \"read\" it would let me do the following:

case class Pers         


        
6条回答
  •  独厮守ぢ
    2020-12-13 21:17

    Scala does not have type classes, and in this case, you cannot even simulate the type class with a trait that is inherited from, because traits only express methods on an object, meaning that they have to be "owned" by a class, so you cannot put the definition of a "constructor that takes a string as the only argument" (which is what "read" might be called in OOP languages) in a trait.

    Instead, you have to simulate type classes yourself. This is done like so (equivalent Haskell code in comments):

    // class Read a where read :: String -> a
    trait Read[A] { def read(s: String): A }
    
    // instance Read Person where read = ... parser for Person ...
    implicit object ReadPerson extends Read[Person] {
      def read(s: String): Person = ... parser for Person ...
    }
    

    Then, when you have a method that depends on the type class, you have to specify it as an implicit context:

    // readList :: Read a => [String] -> [a]
    // readList ss = map read ss
    def readList[A: Read] (ss: List[String]): List[A] = {
      val r = implicitly[Read[A]] // Get the class instance of Read for type A
      ss.map(r.read _)
    }
    

    The user would probably like a polymorphic method like this for ease of use:

    object read {
      def apply[A: Read](s: String): A = implicitly[Read[A]].read(s)
    }
    

    Then one can just write:

    val person: Person = read[Person]("Person(Bob,42)")
    

    I am not aware of any standard implementation(s) for this type class, in particular.

    Also, a disclaimer: I don't have a Scala compiler and haven't used the language for years, so I can't guarantee that this code compiles.

提交回复
热议问题