Typical classes and records as interfaces

I have a terrible time trying to hook functions at runtime (what I need to do), which may include class type restrictions in the input and output of the function.

In a language like Java, that would be trivial. f1 :: Int -> Num , f2 :: Num -> Num Now we can call f2 . f1 f2 . f1 . If Num was a Java-style interface, this would not work. But class classes do not behave like interfaces.

One thing a type class allows is to avoid converting between data types. We could just convert everything back and forth and get rid of type classes. But the concern is that we create objects everywhere for no reason. Try to avoid this.

I started trying to create a tuple (a, Api a), where Api is a record of functions that will work on a. My guess is probably very similar to how class classes work? But then you are faced with a specific type of problem, and I thought about existentiality. But then I realized that (a, Api a) should be able to completely hide completely, since no one cares, and then Api turns into a regular record of data types, not functions.

And so I can only wonder ... is it laziness that solves this?

 module Main where --data Api a = Api { f1 :: a -> Int, f2 :: a -> String } data Api = Api { f1 :: Int, f2 :: String } data MyData = MyData Int String myf1 (MyData xy) = x myf2 (MyData xy) = y myApi x = Api (myf1 x) (myf2 x) from :: Int -> Api from x = myApi $ MyData x "string" to :: Api -> String to api = f2 api main = print $ to . from $ 5 

So, is it enough (or maybe) enough to realize that it does not need to create an Api value at all, since all we need is a call to myf2 by the value of MyData?

So, the "equivalent" of a Java interface is not a type class, as someone told me, but rather is it a record or data type? And this tape provides "lightness" of the interface?

+6
source share
1 answer

But then I realized that (a, Api a) should be able to completely hide completely, since no one cares, and then Api turns into a regular record of data types, not functions.

Right! The use of such an existential type, which will be performed by a simple data type, is called existential style contouring (see also FAQ ). However, this is not directly related to laziness; you can just as easily represent each field as () -> Result in a strict language. Of course, it would not be so nice to use.

The advantage of a type class is implicit, type-oriented permission: you can simply use the operations directly, as if they were monomorphic to the specific types that you are using, and it works fine, without the need for a separate name for each type for which you want to perform an operation. For example, why class classes are valuable, imagine that Num was implemented as a record; you have to go around the implementation record for each type everywhere. Worse, there is no place to put fromInteger :: (Num a) => Integer -> a , since it is polymorphic as a result and generally does not accept a value of type a !

Another example is something like Ord. You cannot represent Ord as a record, because any Ord instance must work with the very values ​​that are eliminated by this record-type-type conversion. Having Ord as a type-type also allows us to write code that is common to any type of value that can be ordered, which is very valuable, and in this regard, using class types is somewhat similar to OOP interfaces.

However, when there are no real relevant values ​​that they speak of, or all of them come from “outside” to act as an internal state (for example, existential), the type-type simply adds an unnecessary template and much less “first” class. Functions and data types are the real units of abstraction in Haskell; Type classes are just convenience.

So, is it enough (or maybe) enough to realize that it does not need to create an Api value at all, since all we need is a call to myf2 by the value of MyData?

The GHC is likely to create an intermediate data type, but you should not worry too much; The existential language also contains a dictionary of type class instances, which is exactly the same as your Api type. The advantage of this recording interface is not performance, but simplicity, clarity, and composability (it is easy to convert all fields of a record to create a “modified” implementation, but you cannot have a function, class).

+12
source

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


All Articles