Haskell Learning Note II: Custom types

Source: Internet
Author: User
Tags custom name

Content Summary:

Algebraic data type-algebraic data Types;

Custom data type--data keyword; value builder; type variable and type constructor;

Record syntax-a syntactic sugar that simplifies custom data types;

A complete example-purchaseorder definition and simple calculation, unit test;

Algebraic datatype (algebraic data Types)

Why does the data type of Haskell have the name algebraic data type? Thinking back to our junior high school, the first time to learn algebra, the most impressive is that x, Y, z instead of specific numbers, the introduction of the concept of equations,

The problem is abstracted, for example, by using the area of the circle to calculate the formula: an =ΠR2, where r is a letter symbol representing the radius of the circle.

Haskell constructs its own type system by reference to algebraic theory. If the type being constructed consists of certain values, then no type variable is required, and such type is a deterministic type;

Type is composed of a number of deterministic values plus type variables, then this type is not a specific type, but an abstract type, when used in concrete, wait until the type variable is replaced by the specific type, can become a concrete

Type. Empty say no, immediately into the actual example.

Custom data types

First look at the system-defined bool type:

Data Bool = False | True

Explain in detail:

    • Use the keyword data to define a new type;
    • Data followed by the name of the new type, the name must be the beginning of capital letters;
    • After the equals sign, is a new type of optional value expression, also known as value constructors;
    • If there is more than one value constructor, use the ' | ' To divide, represent, or, in a variety of possible meanings;
    • In summary, the definition of the bool type can be interpreted as follows: The Haskell custom name is a type of "Bool" with the value either false or true;

Then look at the custom "Shape" type:

Data Shape = Circle float Float Float | Rectangle Float float float float

Slightly different from the bool type definition, shape has two value constructors, where the first string of each value constructor is its name, followed by the corresponding concrete type;

You can understand the custom shape type in this way:

    • A type with the name "Shape" is customized, and the value may be a circle (circle), or a rectangle (rectangle);
    • If it is circle, then it consists of three float values, which represent the horizontal axis, ordinate, and circle radius of circle.
    • if it is rectangle, then consists of four float values, the first two float represents the rectangle of the upper left point of the horizontal axis, ordinate; Two float represents the horizontal and vertical axis of the lower right of the rectangle.

Write the code above about the shape custom type to the file Shape.hs file, then use GHCI to load (compile), and then look at some of the following interaction results:

-- load SHAPE.HS and compile: L Shape-- first to see if the type True and False is bool:t true--The result: true:: Bool:t False-- The result is: False:: Bool

--and see if the type of circle and rectangle is shape
: T Circle
--The result: Circle:: Shape float, float, float
: T Rectangle
--Result: Rectangle:: Float---

--you can see that both Cirle and rectangle are value constructors, and the return result is shape

Why is the value constructor of the Haskell custom type an uppercase string that represents the name of the value builder? such as the bool type True,false, and the shape type of circle,rectangle;

This name is actually a function name, with the function name plus the specific parameters (possibly, probably not), is to construct the corresponding type of the specific value. This constructs the implementation of different values of specific types, and other languages have a very

Big differences, such as C #, a shape type, cannot have two constructors of different names. This needs to be slowly realized and adapted, at least for the benefit of different constructor names, readability and ideographic.

When Haskell customizes a type, it can also be abstracted with a type variable, such as the maybe type that comes with Haskell, which is defined as follows:

Data maybe a = Nothing | Just A

Every time I see this definition, I feel really cool: A is a type variable that, when you define the maybe type, adds this type variable to build a new type that has two possible values: nothing

Empty, nothing; Just a Just the value builder and wraps the specific type A. As to what type A is, not the maybe type definition, this greatly enriches the connotation of maybe and abstracts the maybe

The essence--either empty or just a thing.

Haskell can deduce some of the specific types of maybe, such as:

1 1 :: Num a ='a'a'  :: Maybe Char:t Nothing-The result is: No: Maybe a, because none has a specific type, so --the value itself is polymorphic

Recording Syntax (record)

In the code that defines shape, circle is followed by three float,rectangle followed by four float, the first time you see this definition, you will be puzzled, what is the meaning of these floats? Do you have a name?

If there are other language backgrounds, especially object-oriented languages like C#,java, we are all familiar with the names of the attributes in the class so that the ideographic and readability are better. In fact, some functional programming languages, such as Erlang,

Haskell, when defining complex or combinatorial types, lacks descriptive support. Fortunately, the record syntax can solve this problem from an indirect level.

For example, if you use the record syntax to define the shape type again:

Data Shape_record = Circle {haxis:: float, Caxis:: Float, Radius:: float}        | Rectangle {lefttopx:: float, lefttopy:: Float, Rightdownx:: Float, Rightdowny:: float} deriving (Show)

In the example above using the record syntax to define a new type, the value constructor name, followed by the curly brace, is the record syntax: a name that starts with a lowercase letter, followed by the corresponding type description. There is a correlation in the type

the name of the field is described, compared to the properties defined in C # or Java, readability and ease of use have been improved.

In essence, the record syntax is just a syntactic sugar, because the name of each value in the type is actually a method that can be from a concrete constructed type instance, or the value of a corresponding field. Like what:

--10.012.05.5}--the result:10.0

A practical example

Suppose an e-commerce company needs to purchase goods from suppliers, generate purchase orders and purchase actions from suppliers. The main contents of the purchase order include: an order number, the supplier's information, the purchase of goods information, etc., assuming

Purchase Order itself has a logical check that the total value of the purchase order equals the sum of the value of all purchased goods (ignoring the actual situation such as freight). The following code shows the definition of a purchase order, and some unit tests ensure that the logical

That's right.

Purchase order (PurchaseOrder) definition, and its function definition for calculating the total value of the order (Poamount):

--purchaseorder.hs file Module PurchaseOrderwhereImport Data.list--Import the Data.list module, you need to use the function defined therein--define the commodity first, i.e. the specific item purchased, using the record syntax definition--Item information includes: Number, description, quantity purchased, unit price, Total price data item=Item {itemnumber:: String, ItemDescription:: String, Ordqty:: Int, UnitPrice:: Float, Extprice:: float} deriving ( Show)

--Define an alias for the list of items for easy reading
Type ItemList = [Item]

--Define purchase orders, use record syntax
--Purchase order information including: order number, supplier number, shipping address, order Total price, purchase item details (is a list)
Data PurchaseOrder = purchaseorder {ponumber:: String, Vendornumber:: String, shiptoaddress:: String
, Poamount:: Float, ItemList:: ItemList} deriving (Show)

--Define two functions to calculate the total price of a purchase order: Logic is simple, that is, the total purchase order price, equal to the sum of the total price of each item
Calculatepoamount ':: PurchaseOrder-Float
Calculatepoamount ' po = foldl (\acc x, acc + x) 0 [Extprice I | | I <-itemList PO]

Calculatepoamount:: PurchaseOrder-PurchaseOrder
Calculatepoamount po = purchaseorder {ponumber = (PONumber po)
, Vendornumber = (Vendornumber po)
, shiptoaddress = (shiptoaddress po)
, Poamount = (calculatepoamount ' po)
, itemList = (itemList po)
}

Next, unit test the above code, the main test two logic: first, the total price of the goods equals the unit price multiplied by the quantity; second, the total price of the purchase order equals the sum of the total price of each item:

--test_purchaseorder.hs 
Module test_purchaseorder where import purchaseorderimport data.list -- build test Databuilddefaulttestitem:: Itembuilddefaulttestitem = Item {itemnumber = " 26-106-016 " = " this is a test item , Ordqty = Span style= "color: #800080;" >100 = 10.12 , Extprice = 1012 }

Buildtestitemlist:: ItemList
Buildtestitemlist = [Builddefaulttestitem | x <-[1..10]]

--Test methods
Checkitemextprice:: Item--Bool
Checkitemextprice item = (fromintegral $ ordqty Item) * (UnitPrice Item) = = (Extprice item)

Checksingleitem:: Bool
Checksingleitem = Checkitemextprice $ builddefaulttestitem

Checkitemlistextprice:: ItemList, Bool
Checkitemlistextprice itemList = and $ map Checkitemextprice itemList

Checkitemlist:: Bool
Checkitemlist = Checkitemlistextprice $ buildtestitemlist

BUILDPO:: PurchaseOrder
Buildpo = purchaseorder {ponumber = "1926543", Vendornumber = "28483", shiptoaddress = "Test address here", itemList = Bui Ldtestitemlist, Poamount = 0.00}

Checkpoamount:: Bool
Checkpoamount = (fromintegral $1012 *) = = (Poamount $ calculatepoamount buildpo)

All_methods_test:: String
All_methods_test = if (and [Checksingleitem, Checkitemlist, Checkpoamount])
Then, "All Pass."
Else "Failed."

Finally, the TEST_PURCHASEORDER.HS is loaded into the ghci, compiled, and then run the All_methods_test method, the results show "all Pass", that is, all the logic of the check is correct.

Haskell Learning Note II: Custom types

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.