Trying to understand class and instance

I'm halfway through the “haskell real world” and thought I would have to write my first code. Not very far...

Basically, I thought that I would try to implement array syntax for paired and double lists so that I could do numerical calculations in lists. Do I believe that a “class” is similar to a common function in CLOS? I made a “class” with the arplus function to add arrays and scalars and did the job for scalars. Now I do not know how to do the same for lists. (Performing the calculation is easy, I just don’t know what to put in place of the “Double” for the list version).

class Narray a where arplus :: a -> a -> a instance Narray Double where arplus ab = a + b 

Then, how do I make mixed versions? Array-scalar and scalar array?

I need something like:

 class Narray a where arplus :: a -> b -> a 

instead

For an example of the code I'm trying to write, see the sbcl code below: (Note: I did this with arrays in lisp, but I did it in lists in haskell as I entered them. Actually, it doesn't matter in terms of question.)

 ;; Generic add for arrays and scalar (defgeneric .+ (ab)) ;; Scalar-scalar (defmethod .+ ((a double-float) (b double-float)) (+ ab)) ;; Array-array (defmethod .+ ((a SB-KERNEL::SIMPLE-ARRAY-DOUBLE-FLOAT) (b SB-KERNEL::SIMPLE-ARRAY-DOUBLE-FLOAT)) (dotimes (i (array-total-size a)) (setf (row-major-aref ai) (+ (row-major-aref ai) (row-major-aref bi)))) a) ;; Array-scaler (defmethod .+ ((a SB-KERNEL::SIMPLE-ARRAY-DOUBLE-FLOAT) (b double-float)) (dotimes (i (array-total-size a)) (setf (row-major-aref ai) (+ (row-major-aref ai) b))) a) ;; Scalar-array (defmethod .+ ((a double-float) (b SB-KERNEL::SIMPLE-ARRAY-DOUBLE-FLOAT)) (dotimes (i (array-total-size b)) (setf (row-major-aref bi) (+ a (row-major-aref bi)))) b) ;; Just to demo the code (defun indgen (n) (let ((r (make-array n :element-type 'double-float))) (dotimes (in) (setf (row-major-aref ri) (coerce i 'double-float))) r)) * (load "arrays.lisp") T * (.+ (indgen 6) 10d0) #(10.0d0 11.0d0 12.0d0 13.0d0 14.0d0 15.0d0) * (.+ (indgen 6) (indgen 6)) #(0.0d0 2.0d0 4.0d0 6.0d0 8.0d0 10.0d0) * 
+4
source share
2 answers

I can't answer the whole question, but for the list version, I got this to work:

 main = [1, 2, 3] `arplus` [4, 5, 6] class Narry a where arplus :: a → a → a instance (Num a) ⇒ Narry [a] where arplus = zipWith (+) 

I also tried turning (Num a) => [a] into a Num instance that had good results.

 instance (Num a) ⇒ Num [a] where (+) = zipWith (+) (*) = zipWith (*) (-) = zipWith (-) negate = map negate abs = map abs signum = map signum fromInteger = repeat∘fromInteger 

You can try it just like

 main = do print $ [1, 2, 3] * 3 -- [3, 6, 9] print $ 3 * [1, 2, 3] -- [3, 6, 9] print $ 3 - [2, 4, 6] -- [-1, 1, 3] print $ [2, 4, 6] + 7 -- [9, 11, 13] print $ abs [-2, 4, -3] -- [2, 4, 3] print $ [1, 2, 3] + [4.3, 5.5, 6.7] -- [5.3, 7.5, 9.7] print $ [1, 2, 3] * [3, 4, 5] -- [3, 8, 15] 

Of course, if you want to do double arithmetic with lists, then you have to write definitions for a few more things. I am not sure what might affect my definition here, although I suspect that this is not much more than what I have demonstrated.

Also, this is not the only way to make Nums lists an instance of Num; probably a better choice than zipWith for (*).

[EDIT] Creating Fractional instance lists is trivially easy and adds even more convenience.

 instance (Fractional a) => Fractional [a] where recip = map recip fromRational = repeat . fromRational 

This makes it possible to use a list with fractional interactions and automatically divides the work!

 ghci> [3, 6, 9] / 3 [1.0,2.0,3.0] ghci> 9 / [1, 2, 3] [9.0,4.5,3.0] ghci> 1.2 + [0, 1, 2] [1.2,2.2,3.2] 

With great power comes great responsibility; use them only if you need them. If you don’t need it, then it’s nice that the compiler yells at you when you try to call + on a number and a list.

+3
source

Basically, I thought that I would try to implement array syntax for paired and double lists so that I could do numerical calculations in lists. Do I believe that a "class" is similar to a common function in CLOS?

Classes like Haskell are a means of implementing what is sometimes called "ad-hoc polymorphism", which means that a function can work on several types, but do different things for each. They are not completely comparable to anything in most other languages, but from what I know little, CLOS "common functions" sound pretty similar.

Then, how do I make mixed versions? Array-scalar and scalar array?

I assume that your arrays and scalars are of different types, which obviously poses a problem because type classes have one type parameter. If you use GHC, there is a MultiParamTypeClasses language MultiParamTypeClasses that does ... exactly what it says basically.

Unfortunately, this can be painful in practice for various reasons, but in this case the problem is that you want the mixed version to work for any order of arguments, but the result should be an array for both. The most straightforward way to do this is to make the result the third type of parameter, but then Haskell cannot know that arplus is applied to the array, and the scalar is an array, since the type parameters are independent of each other. There are other extensions to handle this, but for some tasks the situation can quickly get complicated.

Now, I am too rusty with lisps to be sure that I am reading your example correctly, but here is a blow to what I think you want:

 {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} class Narray abc | ab -> c where arplus :: a -> b -> c instance Narray Double Double Double where arplus = (+) instance Narray Double [Double] [Double] where arplus x = map (x +) instance Narray [Double] Double [Double] where arplus xy = map (+ y) x instance Narray [Double] [Double] [Double] where arplus = zipWith (+) 

You can find the extensions I used in the GHC documentation for more details.

However, please take the above as an example of how to perform overloading in the style of multiple dispatch, rather than necessarily a good idea in practice. As @luqui notes, this type of class combines operations that are conceptually different, and does so for a small gain.

I’m not sure that a discussion of better general design would be useful for you, but suffice it to say that in this case I wouldn’t deal with class types at all.

+3
source

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


All Articles