Composite Algebraic Data Types in Scala and Haskell

When trying to describe a part of Sql with algebraic data types in Scala, I met the need to create subtractions of the root attribute that represents the data type. Since code was created to satisfy this requirement, which I'm not sure can be represented using the Haskell ADT, and since, unlike Haskell, ADT is not a native construct for Scala, I am now interested in:

  • Is it right that it is impossible to imagine a model such as an Sql type having a "subtype" Statement , having a Select constructor in Haskell? (It seems this one might be related).
  • If so, does the term “ADT” refer to the code I created?
  • If so, does this make Scala more powerful than Haskell in this aspect?
  • If this is not the case, what are the reasons why this function will not be implemented in Haskell? It makes me think that I might be too complicated in my model.

Here is the model I'm talking about:

 sealed trait Sql sealed trait Statement extends Sql sealed case class Union ( left : Statement, right : Statement ) extends Statement sealed case class Select ( /** other fields don't matter **/ where : Where ) extends Statement sealed trait Where extends Sql sealed case class And ( left : Where, right : Where ) extends Where sealed case class Or ( left : Where, right : Where ) extends Where sealed case class Equals ( /** fields don't matter **/ ) extends Where 
+6
source share
3 answers

1. No, since your root tag is sealed, it can represent the presented hierarchy as ADT:

 data Sql = Statement Statement | Where Where -- ^ This is the *type* called `Statement` -- ^ This is the *constructor* called `Statement` data Statement = Union Statement Statement | Select Where data Where = And Where Where | Or Where Where | Equals 

In this case, it is possible because you can list all the "subclasses" of your data type ( Sql in this case), which allows you to convert them to ADT constructors. Emulating type hierarchies as ADTs is difficult only if you want the user to add "constructors" / "subclasses" arbitrarily by the user.

2. The term ADT is never applicable to Scala code, since Scala lacks ADT in this language. However, the classes you introduced behave similarly to ADT, so I would say "close enough."

3 and 4. Languages ​​have different strengths and weaknesses.

Haskell can emulate every function of the Scala language, and Scala can emulate every function of the Haskell, since both languages ​​are complete and allow different levels of metaprogramming to be used. Of course, Haskell has a Template Haskell that allows you to emulate something, maybe you can use TH to write Scala code in a Haskell file and compile it as Haskell.

Objects and inheritance are not needed in Haskell, and ADT is mostly not needed in Scala, so there is no reason to compare them. Most object-oriented functions can also be emulated with simple classes like Haskell and data types and using module boundaries. ADT can be emulated in Scala with case classes, and classes of type Haskell can be emulated with implicit parameters and implicit object instances.

However, I would say that in general it is easiest to emulate some Scala functions in Haskell, because Haskell allows for more "implicit language extensions" than Scala. I mean, if you want to emulate Haskell Monad in Scala, you need to write a lot of code in parts that use Monad s, whereas if you want to emulate, say, Scala limited continuations or implicit parameters in Haskell, you can simply write a Monad instance (to continue) or a class with several parameters (for implicit parameters) once for this and the code that you later write in your actual functions will look very close to the Scala code without a large boiler plate. Many, if not most, of the advanced Scala features also come from Haskell or OCaml, so they already exist and don't need to be translated.

In other words: the complex code needed to add a new function needs to be added in only one place in Haskell, after which it can be easily used in several places, while you often have to add a lot of “noise”, everywhere in your Scala code if you want to emulate the Haskell function.

+10
source

You can mimic the Scala design in Haskell, although Haskell encourages the use of qualified and sum types instead of Scala inheritance and subtyping. Based on my limited knowledge of Scala, I wrote the same in Haskell below.

  • Traits are classes
  • Classes are ADT
  • Property Inheritance - Class Member
  • Elements with a typed type are limited existential types.
  • Typeable class used for downcasting
 -- Define traits class Typeable a => Sql a class (Typeable a, Sql a) => Statement a class (Typeable a, Sql a) => Where a -- Define case classes data Union where Union :: (Statement a, Statement b) => a -> b -> Union deriving (Typeable) data Select where Select :: Where a => a -> Select deriving (Typeable) data And where And :: (Where a, Where b) => a -> b -> And deriving (Typeable) data Or where Or :: (Where a, Where b) => a -> b -> Or deriving (Typeable) data Equals where Equals :: Equals deriving (Typeable) -- Define subtyping instance Sql Union instance Statement Union instance Sql Select instance Statement Select instance Sql And instance Where And instance Sql Or instance Where Or instance Sql Equals instance Where Equals 

In Haskell, you are likely to use sum types instead of the Statement and Where classes.

 class Sql a instance Sql Statement instance Sql Where data Statement = Union Statement Statement | Select Where data Where = And Where Where | Or Where Where | Equals 
+4
source

One possible answer to question 4:

Haskell type inference (Hindley-Milner) would have problems with inheritance, so Scala has inheritance, but a less powerful inferencer type.

0
source

Source: https://habr.com/ru/post/923385/


All Articles