How can I create a fixed multidimensional array in Specman/e using varibles?
And then access individual elements or whole rows?
For example in SystemVerilog I would have:
module top;
function automatic my_func();
bit [7:0] arr [4][8]; // matrix: 4 rows, 8 columns of bytes
bit [7:0] row [8]; // array : 8 elements of bytes
row = '{1, 2, 3, 4, 5, 6, 7, 8};
$display("Array:");
foreach (arr[i]) begin
arr[i] = row;
$display("row[%0d] = %p", i, row);
end
$display("\narr[2][3] = %0d", arr[2][3]);
endfunction : my_func
initial begin
my_func();
end
endmodule : top
This will produce this output:
Array:
row[0] = '{'h1, 'h2, 'h3, 'h4, 'h5, 'h6, 'h7, 'h8}
row[1] = '{'h1, 'h2, 'h3, 'h4, 'h5, 'h6, 'h7, 'h8}
row[2] = '{'h1, 'h2, 'h3, 'h4, 'h5, 'h6, 'h7, 'h8}
row[3] = '{'h1, 'h2, 'h3, 'h4, 'h5, 'h6, 'h7, 'h8}
arr[2][3] = 4
Can someone rewrite my_func() in Specman/e?
There are no fixed arrays in e. But you can define a variable of a list type, including a multi-dimensional list, such as:
var my_md_list: list of list of my_type;
It is not the same as a multi-dimensional array in other languages, in the sense that in general each inner list (being an element of the outer list) may be of a different size. But you still can achieve your purpose using it. For example, your code might be rewritten in e more or less like this:
var arr: list of list of byte;
var row: list of byte = {1;2;3;4;5;6;7;8};
for i from 0 to 3 do {
arr.add(row.copy());
print arr[i];
};
print arr[2][3];
Notice the usage of row.copy() - it ensures that each outer list element will be a copy of the original list.
If we don't use copy(), we will get a list of many pointers to the same list. This may also be legitimate, depending on the purpose of your code.
In case of a field (as opposed to a local variable), it is also possible to declare it with a given size. This size is, again, not "fixed" and can be modified at run time (by adding or removing items), but it determines the original size of the list upon creation, for example:
struct foo {
my_list[4][8]: list of list of int;
};
As I told before, I'm working in a library about algebra, matrices and category theory. I have decomposed the algebraic structures in a "tower" of record types, each one representing an algebraic structure. As example, to specify a monoid, we define first a semigroup and to define a commutative monoid we use monoid definition, following the same pattern as Agda standard library.
My trouble is that when I need a property of an algebraic structure that is deep within another one (e.g. property of a Monoid that is part of a CommutativeSemiring) we need to use a number of a projections equal to desired algebraic structure depth.
As an example of my problem, consider the following "lemma":
open import Algebra
open import Algebra.Structures
open import Data.Vec
open import Relation.Binary.PropositionalEquality
open import Algebra.FunctionProperties
open import Data.Product
module _ {Carrier : Set} {_+_ _*_ : Op₂ Carrier} {0# 1# : Carrier} (ICSR : IsCommutativeSemiring _≡_ _+_ _*_ 0# 1#) where
csr : CommutativeSemiring _ _
csr = record{ isCommutativeSemiring = ICSR }
zipWith-replicate-0# : ∀ {n}(xs : Vec Carrier n) → zipWith _+_ (replicate 0#) xs ≡ xs
zipWith-replicate-0# [] = refl
zipWith-replicate-0# (x ∷ xs) = cong₂ _∷_ (proj₁ (IsMonoid.identity (IsCommutativeMonoid.isMonoid
(IsCommutativeSemiring.+-isCommutativeMonoid
(CommutativeSemiring.isCommutativeSemiring csr)))) x)
(zipWith-replicate-0# xs)
Note that in order to access the left identity property of a monoid, I need to project it from the monoid that is within the commutative monoid that lies in the structure of an commutative semiring.
My concern is that, as I add more and more algebraic structures, such lemmas it will become unreadable.
My question is: Is there a pattern or trick that can avoid this "ladder" of record projections?
Any clue on this is highly welcome.
If you look at the Agda standard library, you'll see that for most specialized algebraic structures, the record defining them has the less specialized structure open public. E.g. in Algebra.AbelianGroup, we have:
record AbelianGroup c ℓ : Set (suc (c ⊔ ℓ)) where
-- ... snip ...
open IsAbelianGroup isAbelianGroup public
group : Group _ _
group = record { isGroup = isGroup }
open Group group public using (setoid; semigroup; monoid; rawMonoid)
-- ... snip ...
So an AbelianGroup record will have not just the AbelianGroup/IsAbelianGroup fields available, but also setoid, semigroup and monoid and rawMonoid from Group. In turn, setoid, monoid and rawMonoid in Group come from similarly open public-reexporting these fields from Monoid.
Similarly, for algebraic property witnesses, they re-export the less specialized version's fields, e.g. in IsAbelianGroup we have
record IsAbelianGroup
{a ℓ} {A : Set a} (≈ : Rel A ℓ)
(∙ : Op₂ A) (ε : A) (⁻¹ : Op₁ A) : Set (a ⊔ ℓ) where
-- ... snip ...
open IsGroup isGroup public
-- ... snip ...
and then IsGroup reexports IsMonoid, IsMonoid reexports IsSemigroup, and so on. And so, if you have IsAbelianGroup open, you can still use assoc (coming from IsSemigroup) without having to write out the whole path to it by hand.
The bottom line is you can write your function as follows:
open CommutativeSemiring CSR hiding (refl)
open import Function using (_⟨_⟩_)
zipWith-replicate-0# : ∀ {n}(xs : Vec Carrier n) → zipWith _+_ (replicate 0#) xs ≡ xs
zipWith-replicate-0# [] = refl
zipWith-replicate-0# (x ∷ xs) = proj₁ +-identity x ⟨ cong₂ _∷_ ⟩ zipWith-replicate-0# xs
Ada 2012 user defined iterator
This feature allows user to make a custom iterator. Instead of writing Get (List, Index) one could write List (Index) and instead of writing for Index in 1 .. List.Max one could write for Index in List'Range. I am not sure if List'Range is possible if list is not an array, maybe there is another syntax for user defined iterator.
How to use Ada 2012 user defined iterators?
Preferably: how to implement user defined iterator in the example?
Example
This is a Stack or LIFO example. Next step is to hide type Stack members and implement user defined iterator for type Stack List.
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Main is
use Ada.Text_IO;
use Ada.Integer_Text_IO;
type Integer_Array is array (Integer range <>) of Integer;
type Stack (Count : Natural) is record
List : Integer_Array (1 .. Count);
Top : Natural := 0;
end record;
procedure Push (Item : Integer; Result : in out Stack) is
begin
Result.Top := Result.Top + 1;
Result.List (Result.Top) := Item;
end;
S : Stack (10);
begin
Push (5, S);
Push (3, S);
Push (8, S);
for I in S.List'First .. S.Top loop
Put (S.List (I), 2);
New_Line;
end loop;
--for I in S'Range loop
--Put (S (I), 2);
--New_Line;
--end loop;
end;
The use of S'Range is not available for containers, only for standard arrays. This cannot be changed.
You likely meant to say for C in S.Iterate loop which calls the function Iterate and returns cursors for each step of the loop. At that point, you need to use Element (C) to get access to the actual element (or perhaps more efficiently Reference (C).
A third version is for E of S loop which returns directly the element. You do not have access to the corresponding cursor. This is in general the preferred way to write the loop, except perhaps when iterating over the whole contents of a map, since there is no way to access the key, only the value.
For more information on how to add support for these loops in your own data structures, you could take a look at the two gems published by AdaCore:
http://www.adacore.com/adaanswers/gems/gem-127-iterators-in-ada-2012-part-1/
http://www.adacore.com/adaanswers/gems/gem-128-iterators-in-ada-2012-part-2/
which should provide all the information you need, I hope.
I'm not sure that being able to iterate over the contents of a stack is part of the normal use case for stacks! That aside, this is the sort of code you can write using generalized iteration (ARM 5.5.2):
with Ada.Text_IO;
with Stacks;
procedure Iteration is
use Ada.Text_IO;
S : Stacks.Stack (10);
begin
Stacks.Push (S, 5);
Stacks.Push (S, 3);
Stacks.Push (S, 8);
for C in Stacks.Iterate (S) loop
Put_Line (Stacks.Element (C)'Img); --'
end loop;
end Iteration;
This is a possible spec for Stacks. Note that System is only used in the private part.
with Ada.Iterator_Interfaces;
private with System;
package Stacks is
These are part of the fundamental Stack abstraction. Note that if you wanted to be able to write an indexed access (My_Stack (42) or the loop for E of My_Stack loop...) things get much more complicated. For a start, Stack would have to be tagged.
type Stack (Count : Natural) is private;
procedure Push (To : in out Stack; Item : Integer);
function Element (S : Stack; Index : Positive) return Integer;
The remainder of the public part is to support generalized iteration.
type Cursor is private;
function Has_Element (Pos : Cursor) return Boolean;
function Element (C : Cursor) return Integer;
package Stack_Iterators
is new Ada.Iterator_Interfaces (Cursor, Has_Element);
function Iterate (S : Stack)
return Stack_Iterators.Forward_Iterator’Class; --'
private --'
type Integer_Array is array (Positive range <>) of Integer;
type Stack (Count : Natural) is record
List : Integer_Array (1 .. Count);
Top : Natural := 0;
end record;
function Element (S : Stack; Index : Positive) return Integer
is (S.List (Index));
Cursor needs a reference to the object it's a cursor for. I've used System.Address to avoid using access types and needing to make Stacks aliased.
type Cursor is record
The_Stack : System.Address;
List_Index : Positive := 1;
end record;
end Stacks;
The package body ...
with System.Address_To_Access_Conversions;
package body Stacks is
procedure Push (To : in out Stack; Item : Integer) is
begin
To.Top := To.Top + 1;
To.List (To.Top) := Item;
end Push;
We're going to have to convert Stack addresses to pointers-to-Stacks.
package Address_Conversions
is new System.Address_To_Access_Conversions (Stack);
function Has_Element (Pos : Cursor) return Boolean is
S : Stack renames Address_Conversions.To_Pointer (Pos.The_Stack).all;
begin
return Pos.List_Index <= S.Top;
end Has_Element;
function Element (C : Cursor) return Integer is
S : Stack renames Address_Conversions.To_Pointer (C.The_Stack).all;
begin
return S.List (C.List_Index);
end Element;
Now for the actual iterator; I've only done the forward version.
type Iterator is new Stack_Iterators.Forward_Iterator with record
The_Stack : System.Address;
end record;
overriding function First (Object : Iterator) return Cursor;
overriding function Next (Object : Iterator; Pos : Cursor) return Cursor;
function Iterate (S : Stack)
return Stack_Iterators.Forward_Iterator'Class is --'
begin
-- I seem to be relying on S being passed by reference.
-- This would be OK anyway if Stack was tagged; but it is a
-- reasonable bet that the compiler will pass a large object
-- by reference.
return It : Iterator do
It.The_Stack := S’Address; --'
end return; --'
end Iterate;
function First (Object : Iterator) return Cursor is
begin
return C : Cursor do
C.The_Stack := Object.The_Stack;
C.List_Index := 1;
end return;
end First;
function Next (Object : Iterator; Pos : Cursor) return Cursor is
pragma Unreferenced (Object);
begin
return C : Cursor do
C.The_Stack := Pos.The_Stack;
C.List_Index := Pos.List_Index + 1;
end return;
end Next;
end Stacks;
You use the iterators in for loops like this:
for C in S.Iterator loop
Put (S (C));
New_Line;
end loop;
Or in the more lazy version:
for E of S loop
Put (E);
New_Line;
end loop;
Implementing iterators is a rather long story, and I refer you to the Ada 2012 rationale (which #trashgod also mentioned) for a detailed explanation.
I understand that abstraction is about taking something more concrete and making it more abstract. That something may be either a data structure or a procedure. For example:
Data abstraction: A rectangle is an abstraction of a square. It concentrates on the fact a square has two pairs of opposite sides and it ignores the fact that adjacent sides of a square are equal.
Procedural abstraction: The higher order function map is an abstraction of a procedure which performs some set of operations on a list of values to produce an entirely new list of values. It concentrates on the fact that the procedure loops through every item of the list in order to produce a new list and ignores the actual operations performed on every item of the list.
So my question is this: how is abstraction any different from generalization? I'm looking for answers primarily related to functional programming. However if there are parallels in object-oriented programming then I would like to learn about those as well.
A very interesting question indeed. I found this article on the topic, which concisely states that:
While abstraction reduces complexity by hiding irrelevant detail, generalization reduces complexity by replacing multiple entities which perform similar functions with a single construct.
Lets take the old example of a system that manages books for a library. A book has tons of properties (number of pages, weight, font size(s), cover,...) but for the purpose of our library we may only need
Book(title, ISBN, borrowed)
We just abstracted from the real books in our library, and only took the properties that interested us in the context of our application.
Generalization on the other hand does not try to remove detail but to make functionality applicable to a wider (more generic) range of items. Generic containers are a very good example for that mindset: You wouldn't want to write an implementation of StringList, IntList, and so on, which is why you'd rather write a generic List which applies to all types (like List[T] in Scala). Note that you haven't abstracted the list, because you didn't remove any details or operations, you just made them generically applicable to all your types.
Round 2
#dtldarek's answer is really a very good illustration! Based on it, here's some code that might provide further clarification.
Remeber the Book I mentioned? Of course there are other things in a library that one can borrow (I'll call the set of all those objects Borrowable even though that probably isn't even a word :D):
All of these items will have an abstract representation in our database and business logic, probably similar to that of our Book. Additionally, we might define a trait that is common to all Borrowables:
trait Borrowable {
def itemId:Long
}
We could then write generalized logic that applies to all Borrowables (at that point we don't care if its a book or a magazine):
object Library {
def lend(b:Borrowable, c:Customer):Receipt = ...
[...]
}
To summarize: We stored an abstract representation of all the books, magazines and DVDs in our database, because an exact representation is neither feasible nor necessary. We then went ahead and said
It doesn't matter whether a book, a magazine or a DVD is borrowed by a customer. It's always the same process.
Thus we generalized the operation of borrowing an item, by defining all things that one can borrow as Borrowables.
Object:
Abstraction:
Generalization:
Example in Haskell:
The implementation of the selection sort by using priority queue with three different interfaces:
an open interface with the queue being implemented as a sorted list,
an abstracted interface (so the details are hidden behind the layer of abstraction),
a generalized interface (the details are still visible, but the implementation is more flexible).
{-# LANGUAGE RankNTypes #-}
module Main where
import qualified Data.List as List
import qualified Data.Set as Set
{- TYPES: -}
-- PQ new push pop
-- by intention there is no build-in way to tell if the queue is empty
data PriorityQueue q t = PQ (q t) (t -> q t -> q t) (q t -> (t, q t))
-- there is a concrete way for a particular queue, e.g. List.null
type ListPriorityQueue t = PriorityQueue [] t
-- but there is no method in the abstract setting
newtype AbstractPriorityQueue q = APQ (forall t. Ord t => PriorityQueue q t)
{- SOLUTIONS: -}
-- the basic version
list_selection_sort :: ListPriorityQueue t -> [t] -> [t]
list_selection_sort (PQ new push pop) list = List.unfoldr mypop (List.foldr push new list)
where
mypop [] = Nothing -- this is possible because we know that the queue is represented by a list
mypop ls = Just (pop ls)
-- here we abstract the queue, so we need to keep the queue size ourselves
abstract_selection_sort :: Ord t => AbstractPriorityQueue q -> [t] -> [t]
abstract_selection_sort (APQ (PQ new push pop)) list = List.unfoldr mypop (List.foldr mypush (0,new) list)
where
mypush t (n, q) = (n+1, push t q)
mypop (0, q) = Nothing
mypop (n, q) = let (t, q') = pop q in Just (t, (n-1, q'))
-- here we generalize the first solution to all the queues that allow checking if the queue is empty
class EmptyCheckable q where
is_empty :: q -> Bool
generalized_selection_sort :: EmptyCheckable (q t) => PriorityQueue q t -> [t] -> [t]
generalized_selection_sort (PQ new push pop) list = List.unfoldr mypop (List.foldr push new list)
where
mypop q | is_empty q = Nothing
mypop q | otherwise = Just (pop q)
{- EXAMPLES: -}
-- priority queue based on lists
priority_queue_1 :: Ord t => ListPriorityQueue t
priority_queue_1 = PQ [] List.insert (\ls -> (head ls, tail ls))
instance EmptyCheckable [t] where
is_empty = List.null
-- priority queue based on sets
priority_queue_2 :: Ord t => PriorityQueue Set.Set t
priority_queue_2 = PQ Set.empty Set.insert Set.deleteFindMin
instance EmptyCheckable (Set.Set t) where
is_empty = Set.null
-- an arbitrary type and a queue specially designed for it
data ABC = A | B | C deriving (Eq, Ord, Show)
-- priority queue based on counting
data PQ3 t = PQ3 Integer Integer Integer
priority_queue_3 :: PriorityQueue PQ3 ABC
priority_queue_3 = PQ new push pop
where
new = (PQ3 0 0 0)
push A (PQ3 a b c) = (PQ3 (a+1) b c)
push B (PQ3 a b c) = (PQ3 a (b+1) c)
push C (PQ3 a b c) = (PQ3 a b (c+1))
pop (PQ3 0 0 0) = undefined
pop (PQ3 0 0 c) = (C, (PQ3 0 0 (c-1)))
pop (PQ3 0 b c) = (B, (PQ3 0 (b-1) c))
pop (PQ3 a b c) = (A, (PQ3 (a-1) b c))
instance EmptyCheckable (PQ3 t) where
is_empty (PQ3 0 0 0) = True
is_empty _ = False
{- MAIN: -}
main :: IO ()
main = do
print $ list_selection_sort priority_queue_1 [2, 3, 1]
-- print $ list_selection_sort priority_queue_2 [2, 3, 1] -- fail
-- print $ list_selection_sort priority_queue_3 [B, C, A] -- fail
print $ abstract_selection_sort (APQ priority_queue_1) [B, C, A] -- APQ hides the queue
print $ abstract_selection_sort (APQ priority_queue_2) [B, C, A] -- behind the layer of abstraction
-- print $ abstract_selection_sort (APQ priority_queue_3) [B, C, A] -- fail
print $ generalized_selection_sort priority_queue_1 [2, 3, 1]
print $ generalized_selection_sort priority_queue_2 [B, C, A]
print $ generalized_selection_sort priority_queue_3 [B, C, A]-- power of generalization
-- fail
-- print $ let f q = (list_selection_sort q [2,3,1], list_selection_sort q [B,C,A])
-- in f priority_queue_1
-- power of abstraction (rank-n-types actually, but never mind)
print $ let f q = (abstract_selection_sort q [2,3,1], abstract_selection_sort q [B,C,A])
in f (APQ priority_queue_1)
-- fail
-- print $ let f q = (generalized_selection_sort q [2,3,1], generalized_selection_sort q [B,C,A])
-- in f priority_queue_1
The code is also available via pastebin.
Worth noticing are the existential types. As #lukstafi already pointed out, abstraction is similar to existential quantifier and generalization is similar to universal quantifier.
Observe that there is a non-trivial connection between the fact that ∀x.P(x) implies ∃x.P(x) (in a non-empty universe), and that there rarely is a generalization without abstraction (even c++-like overloaded functions form a kind of abstraction in some sense).
Credits:
Portal cake by Solo.
Dessert table by djttwo.
The symbol is the cake icon from material.io.
I'm going to use some examples to describe generalisation and abstraction, and I'm going to refer to this article.
To my knowledge, there is no official source for the definition of abstraction and generalisation in the programming domain (Wikipedia is probably the closest you'll get to an official definition in my opinion), so I've instead used an article which I deem credible.
Generalization
The article states that:
"The concept of generalization in OOP means that an object encapsulates
common state and behavior for a category of objects."
So for example, if you apply generalisation to shapes, then the common properties for all types of shape are area and perimeter.
Hence a generalised shape (e.g. Shape) and specialisations of it (e.g. a Circle), can be represented in classes as follows (note that this image has been taken from the aforementioned article)
Similarly, if you were working in the domain of jet aircraft, you could have a Jet as a generalisation, which would have a wingspan property. A specialisation of a Jet could be a FighterJet, which would inherit the wingspan property and would have its own property unique to fighter jets e.g. NumberOfMissiles.
Abstraction
The article defines abstraction as:
"the process of identifying common patterns that have systematic
variations; an abstraction represents the common pattern and provides
a means for specifying which variation to use" (Richard Gabriel)"
In the domain of programming:
An abstract class is a parent class that allows inheritance but can
never be instantiated.
Hence in the example given in the Generalization section above, a Shape is abstract as:
In the real world, you never calculate the area or perimeter of a
generic shape, you must know what kind of geometric shape you have
because each shape (eg. square, circle, rectangle, etc.) has its own
area and perimeter formulas.
However, as well as being abstract a shape is also a generalisation (because it "encapsulates common state and behavior for a category of objects" where in this case the objects are shapes).
Going back to the example I gave about Jets and FighterJets, a Jet is not abstract as a concrete instance of a Jet is feasible, as one can exist in the real world, unlike a shape i.e. in the real world you cant hold a shape you hold an instance of a shape e.g. a cube. So in the aircraft example, a Jet is not abstract, it is a generalisation as it is possible to have a "concrete" instance of a jet.
Not addressing credible / official source: an example in Scala
Having "Abstraction"
trait AbstractContainer[E] { val value: E }
object StringContainer extends AbstractContainer[String] {
val value: String = "Unflexible"
}
class IntContainer(val value: Int = 6) extends AbstractContainer[Int]
val stringContainer = new AbstractContainer[String] {
val value = "Any string"
}
and "Generalization"
def specialized(c: StringContainer.type) =
println("It's a StringContainer: " + c.value)
def slightlyGeneralized(s: AbstractContainer[String]) =
println("It's a String container: " + s.value)
import scala.reflect.{ classTag, ClassTag }
def generalized[E: ClassTag](a: AbstractContainer[E]) =
println(s"It's a ${classTag[E].toString()} container: ${a.value}")
import scala.language.reflectiveCalls
def evenMoreGeneral(d: { def detail: Any }) =
println("It's something detailed: " + d.detail)
executing
specialized(StringContainer)
slightlyGeneralized(stringContainer)
generalized(new IntContainer(12))
evenMoreGeneral(new { val detail = 3.141 })
leads to
It's a StringContainer: Unflexible
It's a String container: Any string
It's a Int container: 12
It's something detailed: 3.141
Abstraction
Abstraction is specifying the framework and hiding the implementation level information. Concreteness will be built on top of the abstraction. It gives you a blueprint to follow to while implementing the details. Abstraction reduces the complexity by hiding low level details.
Example: A wire frame model of a car.
Generalization
Generalization uses a “is-a” relationship from a specialization to the generalization class. Common structure and behaviour are used from the specializtion to the generalized class. At a very broader level you can understand this as inheritance. Why I take the term inheritance is, you can relate this term very well. Generalization is also called a “Is-a” relationship.
Example: Consider there exists a class named Person. A student is a person. A faculty is a person. Therefore here the relationship between student and person, similarly faculty and person is generalization.
I'd like to offer an answer for the greatest possible audience, hence I use the Lingua Franca of the web, Javascript.
Let's start with an ordinary piece of imperative code:
// some data
const xs = [1,2,3];
// ugly global state
const acc = [];
// apply the algorithm to the data
for (let i = 0; i < xs.length; i++) {
acc[i] = xs[i] * xs[i];
}
console.log(acc); // yields [1, 4, 9]
In the next step I introduce the most important abstraction in programming - functions. Functions abstract over expressions:
// API
const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
const concat = xs => ys => xs.concat(ys);
const sqr_ = x => [x * x]; // weird square function to keep the example simple
// some data
const xs = [1,2,3];
// applying
console.log(
foldr(x => acc => concat(sqr_(x)) (acc)) ([]) (xs) // [1, 4, 9]
)
As you can see a lot of implementation details are abstracted away. Abstraction means the suppression of details.
Another abstraction step...
// API
const comp = (f, g) => x => f(g(x));
const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
const concat = xs => ys => xs.concat(ys);
const sqr_ = x => [x * x];
// some data
const xs = [1,2,3];
// applying
console.log(
foldr(comp(concat, sqr_)) ([]) (xs) // [1, 4, 9]
);
And another one:
// API
const concatMap = f => foldr(comp(concat, f)) ([]);
const comp = (f, g) => x => f(g(x));
const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
const concat = xs => ys => xs.concat(ys);
const sqr_ = x => [x * x];
// some data
const xs = [1,2,3];
// applying
console.log(
concatMap(sqr_) (xs) // [1, 4, 9]
);
The underlying principle should now be clear. I'm still dissatisfied with concatMap though, because it only works with Arrays. I want it to work with every data type that is foldable:
// API
const concatMap = foldr => f => foldr(comp(concat, f)) ([]);
const concat = xs => ys => xs.concat(ys);
const sqr_ = x => [x * x];
const comp = (f, g) => x => f(g(x));
// Array
const xs = [1, 2, 3];
const foldr = f => acc => xs => xs.reduceRight((acc, x) => f(x) (acc), acc);
// Option (another foldable data type)
const None = r => f => r;
const Some = x => r => f => f(x);
const foldOption = f => acc => tx => tx(acc) (x => f(x) (acc));
// applying
console.log(
concatMap(foldr) (sqr_) (xs), // [1, 4, 9]
concatMap(foldOption) (sqr_) (Some(3)), // [9]
concatMap(foldOption) (sqr_) (None) // []
);
I broadened the application of concatMap to encompass a larger domain of data types, nameley all foldable datatypes. Generalization emphasizes the commonalities between different types, (or rather objects, entities).
I achieved this by means of dictionary passing (concatMap's additional argument in my example). Now it is somewhat annoying to pass these type dicts around throughout your code. Hence the Haskell folks introduced type classes to, ...um, abstract over type dicts:
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\x -> [x * x]) ([1,2,3]) -- yields [1, 4, 9]
concatMap (\x -> [x * x]) (Just 3) -- yields [9]
concatMap (\x -> [x * x]) (Nothing) -- yields []
So Haskell's generic concatMap benefits from both, abstraction and generalization.
Let me explain in the simplest manner possible.
"All pretty girls are female." is an abstraction.
"All pretty girls put on make-up." is a generalization.
Abstraction is usually about reducing complexity by eliminating unnecessary details. For example, an abstract class in OOP is a parent class that contains common features of its children but does not specify the exact functionality.
Generalization does not necessarily require to avoid details but rather to have some mechanism to allow for applying the same function to different argument. For instance, polymorphic types in functional programming languages allow you not to bother about the arguments, rather focus on the operation of the function. Similarly, in java you can have generic type which is an "umbrella" to all types while the function is the same.
Here is a more general (pun intended) description.
abstraction operation
changes the representation of an entity by hiding/reducing its properties that are not necessary for the desired conceptualization
has an inherent information loss, which makes it less flexible but more conclusive
generalization operation
doesn't change the representation of an entity, but defines similarities between entities of different kind
applies knowledge previously acquired to unseen circumstances or extends that knowledge beyond the scope of the original problem (knowledge transfer)
can be seen as a hypothesis that a set of entities of different kind have similar properties and will behave consistently when applied in a certain way
has no inherent information loss, which makes it more flexible but less conclusive (more error-prone)
Both operations reduce complexity either by hiding details or by reducing entities that perform similar functions to a single construct.
The c++ code which i wanted to rewrite or convert is:
class numberClass
{
private:
int value;
public:
int read()
{
return value;
}
void load(int x)
{
value = x;
}
void increment()
{
value= value +1;
}
};
int main()
{
numberClass num;
num.load(5);
int x=num.read();
cout<<x<<endl;
num.increment();
x=num.read();
cout<<x;
}
I do not know how to make any entity(like variable in C++) that can hold value throughout the program in haskell.
Please help.
Thanks
Basically, you can't. Values are immutable, and Haskell has no variables in the sense of boxes where you store values, like C++ and similar. You can do something similar using IORefs (which are boxes you can store values in), but it's almost always a wrong design to use them.
Haskell is a very different programming language, it's not a good idea to try to translate code from a language like C, C++, Java or so to Haskell. One has to view the tasks from different angles and approach it in a different way.
That being said:
module Main (main) where
import Data.IORef
main :: IO ()
main = do
num <- newIORef 5 :: IO (IORef Int)
x <- readIORef num
print x
modifyIORef num (+1)
x <- readIORef num
print x
Well, assuming that it's the wrapping, not the mutability, you can easily have a type that only allows constructing constant values and incrementation:
module Incr (Incr, incr, fromIncr, toIncr) where
newtype Incr a = Incr a deriving (Read, Show)
fromIncr :: Incr a -> a
fromIncr (Incr x) = x
incr :: (Enum a) => Incr a -> Incr a
incr (Incr x) = Incr (succ x)
toIncr :: a -> Incr a
toIncr = Incr
As Daniel pointed out, mutability is out of the question, but another purpose of your class is encapsulation, which this module provides just like the C++ class. Of course to a Haskell programmer, this module might not seem very useful, but perhaps you have use cases in mind, where you want to statically prevent library users from using regular addition or multiplication.
A direct translation of your code to haskell is rather stupid but of course possible (as shown in Daniel's answer).
Usually when you are working with state in haskell you might be able to work with the State Monad. As long as you are executing inside the State Monad you can query and update your state. If you want to be able to do some IO in addition (as in your example), you need to stack your State Monad on top of IO.
Using this approach your code might look like this:
import Control.Monad.State
import Prelude hiding(read)
increment = modify (+1)
load = put
read = get
normal :: StateT Int IO ()
normal = do
load 5
x <- read
lift (print x)
increment
x <- read
lift (print x)
main = evalStateT normal 0
But here you don't have an explicit type for your numberClass. If you want this there is a nice library on hackage that you could use: data-lenses.
Using lenses the code might be a little closer to your C++ version:
{-# LANGUAGE TemplateHaskell #-}
import Control.Monad.State(StateT,evalStateT,lift)
import Prelude hiding(read)
import Data.Lens.Lazy((~=),access,(%=))
import Data.Lens.Template(makeLenses)
data Number = Number {
_value :: Int
} deriving (Show)
$( makeLenses [''Number] )
increment = value %= succ
load x = value ~= x
read = access value
withLens :: StateT Number IO ()
withLens = do
load 5
x <- read
lift $ print x
increment
x <- read
lift $ print x
main = evalStateT withLens (Number 0)
Still not exactly your code...but well, it's haskell and not yet another OO-language.