Scala - Modeling domain objects

I'm currently trying to combine my mind around Scala with the intention of using it for my next project, which is supposed to deal with DICOM. DICOM has a fairly broad specification, which covers more than a thousand pages of the standard. My understanding of DICOM is quite limited, but in short, DICOM objects β€” IODs (definition of an informational object) β€”consist of modules, and modules are a set of name-value attribute pairs. This is further complicated by the optionality of certain modules and attributes. For example:

SimpleImageIOD: { PatientModule: { name: String dateOfBirth: DateTime } StudyModule: { name: String date: DateTime (optional) } SeriesModule: { name: String } ImageModule: { height: Integer width: Integer pixelSize: Double (optional) } EquipmentModule: { (optional) type: String } } 

There are tons of modules, they can be composed in various combinations forming different IODs. Scala, in turn, has many modeling capabilities with all the features, class classes, dynamic classes, etc. How could you model such a domain in Scala? I am new to the language, but I thought of using immutable case classes to define modules, then combining them into different IODs and using lenses to update:

 case class Patient(name: String, dateOfBirth: DateTime) case class Study(name: String, date: Option[DateTime]) case class Series(name: String) case class Image(height: Integer, width: Integer, pixelSize: Option[Double]) case class Equipment(type: String) case class SimpleImageIOD(patient: Patient, study: Study, series: Series, image: Image, equipment: Option[Equipment]) object Patient { val nameL: Lens[Patient, String] = ... val dateOfBirthL: Lens[Patient, DateTime] = ... } object SimpleImageIOD { val patientL: Lens[SimpleImageIOD, Patient] = ... val patientNamel = patientL andThen Patient.nameL } 

Etc etc. As for lenses, the problem of coding the entire template may arise - there will be an order of M x N x L lenses to cover the entire domain for the M IOD, N and L modules M Also, the optionality of some fields complicates the task, at least with scalaz-seven .

What other viable approaches are available out there that are in line with the spirit of Scala?

+7
source share
1 answer

You can insert lenses, so I don’t think you will have M x N x L lenses. I think this is a very reasonable approach.

An alternative approach is to define your own one-time update methods, for example:

 case class Patient(name: String, dateOfBirth: Long) { def name(f: String => String): Patient = copy(name = f(name)) def dateOfBirth(f: Long => Long): Patient = copy(dateOfBirth = f(dateOfBirth)) } 

which you use like this:

 scala> Patient("John",0).name(_ + " Smith") res1: Patient = Patient(John Smith,0) 

This is a kind of achievement with lenses, but you have to be very careful to avoid a sharp increase in the number of patterns (both by use and by definition). It does not have the flexibility of lenses, where you can arbitrarily share the possibilities (for example, updating), but this compensates for this through optimization.

I do most of my deep update this way:

 myImage.patient(_.study(_.date(_ => Some(System.currentTimeMillis)))) 

- all you need to update the whole tree with a study with the current time (pretending here that DateTime is actually Long ), and if you really need to take this opportunity to restore this tree in the patch is more natural with lenses), could you

 val setStudyTime = (t: Long) => myImage.patient(_.study(_.date(_ => Some(t)))) 

or even

 def studyTimeSetter(si: SimpleImageIOD) = (t: Long) => si.patient(_.study(_.date(_ => Some(t)))) 

if you need to quickly get this opportunity at the time of notification.

If you need everything that lenses offer you, then it works more to rebuild it in a special way using this approach, but if you need only a small part and basically just need to knock down the template for installation, pretty good work.

+5
source

All Articles