juan_gandhi: (VP)
Juan-Carlos Gandhi ([personal profile] juan_gandhi) wrote2014-08-28 03:40 pm
Entry tags:

handling a pointer in Scala code

A pointer can be an ID of something (people like ids), or a Url, whatever.

Approach 1. Native type. String for url, Long for id. So, you become just a number; your id can be multiplied by 0 or by 7; and a reference to a page has method like substring() or replaceAll(), or +. We can add a url and user id, and it will be okay in this world. Typeless!

Approach 2. Specialized type. case class ID(private val value: Long), class Url(private val text: String); this makes our code more sane. We only compare urls with urls and ids with ids; and we can stash them in sets or maps, using as keys (ok, need hashCode and equals).

This week I had made two errors in my code; in one I had mixed ID of a company and ID of a "connector table entry" that would point to a company; in another I had mixed Url of the main page and Url of a pdf file; in the latter case I just used an identifier, url, and it was a wrong identifier.

Approach 3. Name variables right. If it is an identifier of a claim pdf, call it urlOfClaimPdf. Makes sense; but not always: we can have some method that does not care if it is a corn pdf or a porn pdf. It only needs to be a pdf.

So, approach 4. Phantom types they are called, are not they?
Introduce parameters in these types: IDof[User], IDof[Pdf], or UrlOf[ClaimPdf] where ClaimPdf <: Pdf, and UrlOf[+T] is covariant. On many occasions it will be just a compiler warning, but well, it's way better to have a warning than to go blind, right?

Other solutions?

[identity profile] http://users.livejournal.com/_xacid_/ 2014-08-29 09:24 pm (UTC)(link)
Я конечно не знаю всех деталей задачи поэтому рассуждаю только в рамках вышесказанного. С целью невозможности перепутать типы я бы взял вариант 2, немного его усилил бы и если предельно упростить (и добавить некоторую абстракцию от деталей реализации), начал бы с примерно с такой модели данных
trait Domain {

  object Company {
    case class Id(id: TId)
    case class Name(name: TName)
    case class Url(url: TUrl)

    case class Company(id: Id, name: Name, url: Url)

    def apply(id: TId, name: TName, url: TUrl) =
      Company(Id(id), Name(name), Url(url))
  }

  object User {
    case class Id(id: TId)
    case class Name(name: TName)
    case class User(id: Id, name: Name)

    def apply(id: TId, name: TName) =
      User(Id(id), Name(name))
  }

  type Company = Company.Company
  type User = User.User

  type TId
  type TName
  type TUrl
}

trait DomainDef extends Domain {
  type TId = Int
  type TName = String
  type TUrl = java.net.URL
}


это конечно возможно несколько грубый подход но имхо достаточно надежный для начала
Edited 2014-08-29 21:25 (UTC)