The SDLFileManager is an RPC manager for the remote file system. After it starts, it will attempt to communicate with the remote file system to get the names of all files. Deleting and Uploading will them queue these changes as transactions. If a delete succeeds, the local list of remote files will remove that file name, and likewise, if an upload succeeds, the local list of remote files will now include that file name.
A set of all names of files known on the remote head unit. Known files can be used or deleted on the remote system.
@property (nonatomic, copy, readonly) NSSet<SDLFileName *> *_Nonnull remoteFileNames;
var remoteFileNames: Set<String> { get }
The number of bytes still available for files for this app.
@property (nonatomic, readonly) NSUInteger bytesAvailable;
var bytesAvailable: UInt { get }
The state of the file manager.
@property (nonatomic, copy, readonly) NSString *_Nonnull currentState;
var currentState: String { get }
The currently pending transactions (Upload, Delete, and List Files) in the file manager
@property (nonatomic, copy, readonly) NSArray<__kindof NSOperation *> *_Nonnull pendingTransactions;
var pendingTransactions: [Operation] { get }
Whether or not the file manager is suspended. If suspended, the file manager can continue to queue uploads and deletes, but will not actually perform any of those until it is no longer suspended. This can be used for throttling down the file manager if other, important operations are taking place over the accessory connection.
@property (nonatomic) BOOL suspended;
var suspended: Bool { get set }
Initialize the class…or not, since this method is unavailable. Dependencies must be injected using initWithConnectionManager:
- (nonnull instancetype)init;
nil
Creates a new file manager with a specified connection manager and configuration
- (nonnull instancetype)
initWithConnectionManager:(nonnull id<SDLConnectionManagerType>)manager
configuration:
(nonnull SDLFileManagerConfiguration *)configuration;
A connection manager to use to forward on RPCs
A configuration for this file manager session
An instance of SDLFileManager
The manager stars up and attempts to fetch its initial list and transfer initial files.
- (void)startWithCompletionHandler:
(nullable SDLFileManagerStartupCompletionHandler)completionHandler;
func start() async throws -> Bool
The handler called when the manager is set up or failed to set up with an error. Use weak self when accessing self from the completion handler.
Cancels all file manager operations and deletes all associated data.
- (void)stop;
func stop()
Check if the remote system contains a file
- (BOOL)hasUploadedFile:(nonnull SDLFile *)file;
func hasUploadedFile(_ file: SDLFile) -> Bool
The file to check
Whether or not the remote system has the file
Delete a file stored on the remote system
- (void)deleteRemoteFileWithName:(nonnull SDLFileName *)name
completionHandler:
(nullable SDLFileManagerDeleteCompletionHandler)completion;
func delete(fileName name: String) async throws -> (Bool, UInt)
The name of the remote file. It should be a name currently stored in remoteFileNames
An optional completion handler that sends an error should one occur.
Deletes an array of files on the remote file system. The files are deleted in the order in which they are added to the array, with the first file to be deleted at index 0. The delete queue is sequential, meaning that once a delete request is sent to Core, the queue waits until a response is received from Core before the next delete request is sent.
- (void)deleteRemoteFilesWithNames:(nonnull NSArray<SDLFileName *> *)names
completionHandler:
(nullable SDLFileManagerMultiDeleteCompletionHandler)
completionHandler;
func delete(fileNames names: [String]) async throws
The names of the files to be deleted
an optional SDLFileManagerMultiDeleteCompletionHandler
Upload a file to the remote file system. If a file with the [SDLFile name] already exists, this will overwrite that file. If you do not want that to happen, check remoteFileNames before uploading, or change allowOverwrite to NO.
- (void)uploadFile:(nonnull SDLFile *)file
completionHandler:
(nullable SDLFileManagerUploadCompletionHandler)completion;
func upload(file: SDLFile) async throws -> (Bool, UInt)
An SDLFile that contains metadata about the file to be sent
An optional completion handler that sends an error should one occur.
Uploads an array of files to the remote file system. The files will be uploaded in the order in which they are added to the array, with the first file to be uploaded at index 0. The upload queue is sequential, meaning that once a upload request is sent to Core, the queue waits until a response is received from Core before the next upload request is sent.
The optional progress handler can be used to keep track of the upload progress. After each file upload, the progress handler returns the upload percentage and an error, if one occurred during the upload process. The progress handler also includes an option to cancel the upload of all remaining files in queue.
- (void)uploadFiles:(nonnull NSArray<SDLFile *> *)files
progressHandler:
(nullable SDLFileManagerMultiUploadProgressHandler)progressHandler
completionHandler:
(nullable SDLFileManagerMultiUploadCompletionHandler)completionHandler;
func upload(files: [SDLFile], progressHandler: SDLFileManagerMultiUploadProgressHandler?) async throws
An array of SDLFiles to be sent
an optional SDLFileManagerMultiUploadProgressHandler
an optional SDLFileManagerMultiUploadCompletionHandler
Uploads an array of files to the remote file system. The files will be uploaded in the order in which they are added to the array, with the first file to be uploaded at index 0. The upload queue is sequential, meaning that once an upload request is sent to Core, the queue waits until a response is received from Core before the next upload request is sent.
- (void)uploadFiles:(nonnull NSArray<SDLFile *> *)files
completionHandler:
(nullable SDLFileManagerMultiUploadCompletionHandler)completionHandler;
func upload(files: [SDLFile]) async throws
An array of SDLFiles to be sent
An optional SDLFileManagerMultiUploadCompletionHandler
Check if an SDLFile needs to be uploaded to Core or not. This method differs from hasUploadedFile() because it takes the isStaticIcon
and overwrite
properties into consideration.
For example, if the file is static icon, the method always returns false.
If the file is dynamic, it returns true in one of these situations: 1) the file has the overwrite property set to true, 2) the file hasn’t been uploaded to Core before.
- (BOOL)fileNeedsUpload:(nullable SDLFile *)file;
func fileNeedsUpload(_ file: SDLFile?) -> Bool
the SDLFile that needs to be checked
BOOL that tells whether file needs to be uploaded to Core or not
Uploads an artwork file to the remote file system and returns the name of the uploaded artwork once completed. If an artwork with the same name is already on the remote system, the artwork is not uploaded and the artwork name is simply returned.
- (void)uploadArtwork:(nonnull SDLArtwork *)artwork
completionHandler:
(nullable SDLFileManagerUploadArtworkCompletionHandler)completion;
func upload(artwork: SDLArtwork) async throws -> (Bool, String, UInt)
A SDLArwork containing an image to be sent
An optional completion handler that returns the name of the uploaded artwork. It also returns an error if the upload fails.
Uploads an array of artworks to the remote file system. The artworks will be uploaded in the order in which they are added to the array, with the first file to be uploaded at index 0. The upload queue is sequential, meaning that once an upload request is sent to Core, the queue waits until a response is received from Core before the next upload request is sent.
- (void)uploadArtworks:(nonnull NSArray<SDLArtwork *> *)artworks
completionHandler:
(nullable SDLFileManagerMultiUploadArtworkCompletionHandler)completion;
func upload(artworks: [SDLArtwork]) async throws -> [String]
An array of SDLArtworks to be sent
An optional SDLFileManagerMultiUploadArtworkCompletionHandler
Uploads an array of artworks to the remote file system. The artworks will be uploaded in the order in which they are added to the array, with the first file to be uploaded at index 0. The upload queue is sequential, meaning that once an upload request is sent to Core, the queue waits until a response is received from Core before the next upload request is sent.
The optional progress handler can be used to keep track of the upload progress. After each artwork upload, the progress handler returns the artwork name, the upload percentage and an error, if one occurred during the upload process. The progress handler also includes an option to cancel the upload of all remaining files in queue.
- (void)uploadArtworks:(nonnull NSArray<SDLArtwork *> *)artworks
progressHandler:
(nullable SDLFileManagerMultiUploadArtworkProgressHandler)
progressHandler
completionHandler:
(nullable SDLFileManagerMultiUploadArtworkCompletionHandler)completion;
func upload(artworks: [SDLArtwork], progressHandler: SDLFileManagerMultiUploadArtworkProgressHandler?) async throws -> [String]
An array of SDLArtworks to be sent
An optional SDLFileManagerMultiUploadArtworkProgressHandler
An optional SDLFileManagerMultiUploadArtworkCompletionHandler
A URL to the directory where temporary files are stored. When an SDLFile is created with NSData, it writes to a temporary file until the file manager finishes uploading it.
The SDL library manages the creation and deletion of these files and you should not have to touch this directory at all.
+ (nonnull NSURL *)temporaryFileDirectory;
class func temporaryFileDirectory() -> URL
An NSURL pointing to the location on disk where SDL’s temporary files are stored.


body{ background-color: #F77737;}
Welcome to a therapeutic virtual resort. If you're frustrated with life, you have arrived to the right place .
Here, you can talk about your issues in a safe space with no judgement.
CAPS: Offers counseling and psycholigical services to students. CAPS helps Duke Students enhance strengths and develop abilities to successfully live, grow and learn in their personal and academic lives. We offer many services to Duke undergraduate, graduate, and professional students, including brief individual and group counseling, couples counseling and more. CAPS staff also provide outreach to student groups, particularly programs supportive of at-risk populations, on a wide range of issues impacting them in various aspects of campus life. 3rd Floor, Duke Student Wellness Center, 305 Towerview Drive
The Oasis: Through the promotion of environmental wellness, this self-care space allow you to interact with different items from around the world that promote overall well-being and stress reduction. Create your own aromatherapy room spray, meditate using a singing bowl, or relax in the massage chair.Relax. Reenergize. Repeat.Student Wellness Center, First Floor, Room
Academic Resource Center: The Academic Resource Center (the ARC) offers services to support you academically during your undergraduate career at Duke. ARC services are available free to any Duke undergraduate student, in any year, studying in any discipline. You can learn how to manage your time, develop stronger academic skills and strategies, understand your unique learning style, get a peer tutor, meet with a learning consultant, join a study group. East Campus behind Marketplace
Nutrition Services: We can assist you with a range of options such as dietary intervention for high cholesterol, high triglycerides, diabetes, hypertension, weight management, disordered eating, sports performance issues, or any dietary topic that you might be concerned about. Some people just come in to see if they are eating well! Located in the Student Wellness Center through appointment
Women's Center: The Duke University Women's Center is dedicated to helping every woman at Duke become self-assured with a streetwise savvy that comes from actively engaging with the world. We welcome men and women alike who are committed to gender equity and social change. Students are welcome to hang out in the center between or after classes. Use the computer stations, nap on the couch, or help yourself to some freshly brewed coffee. The Women's Center provides confidential crisis support for people of all genders who are survivors of gender violence, including sexual assault, stalking or unwanted contact, harassment, or intimate partner violence. Crowell Building underneath the Duke CoffeeHouse


---
layout: post
status: publish
published: true
title: Two Bits on Windows 8
author: Arthur Rosa
author_login: arthurlockman
author_email<EMAIL_ADDRESS>author_url: https://b.rthr.me
wordpress_id: 455
wordpress_url: http://b.rthr.me/?p=455
date: '2013-05-14 20:25:35 -0400'
date_gmt: '2013-05-15 00:25:35 -0400'
image: https://static.rthr.me/2013/05/Windows-8-logo-300x300.jpg
tags:
- Microsoft
- Windows
---
I have two specific things to say about Windows 8. It's been getting a lot of hate since its release for a number of reasons, but I believe that it is at least in some ways a sizable improvement over previous Windows versions. Here's my two bits.
- The Metro UI is awful. It makes absolutely no sense on a desktop machine. I can see where it might make sense on a tablet, but the use case for a tablet is very different than the use case for a desktop. When I hit the Windows key, I want a list of my applications to come up, or at least some kind of usable launcher. What I get instead is the start screen, which is almost completely useless on a desktop. I do appreciate the removal of Aero and movement to more of a flat interface, however it's counteracted by the abomination of the start screen.
- In a virtual machine, it's great. I use Windows inside a Parallels virtual machine pretty much exclusively. I only need it to run a few Windows-only applications that I have to use. Windows 7 was terrible at this. It was horribly slow, didn't adapt to changing network configurations, and froze up constantly just trying to do menial tasks like copying files. Windows 8 on the other hand is snappy, light on resources, and honestly pleasing to the eye when used with Parallels coherence mode. I use it in that mode almost all the time, and it looks fantastic. Windows 7 looked and worked horribly in that mode. As a VM, Windows 8 is stellar. I won't go back unless I absolutely have to.
I appreciate what Microsoft tried to do with Windows 8, and I think it's a step in the right direction for them as a company. With further improvements, I really believe that Windows 8 can be great someday. I still would never use it as a primary OS, though.


News story
The first reference tests in English and maths will take place in March 2017 and in future years will be used when awarding GCSEs.
We are introducing the new national reference tests to provide additional information to support the awarding of GCSEs. The purpose of the tests is to provide evidence on changes in performance standards over time in English language and maths in England at the end of year 11. The tests should allow us to see, over several years, if there is a change in how students perform at the national level, which we will consider carefully in our awarding.
The tests will provide additional information that we expect will significantly advance our ability to detect genuine changes in performance and so improve awarding. We will act cautiously as we build our understanding of the information that the tests will provide and how this is used in awarding GCSEs.
There will be separate tests for maths and English. They will be based on the new content for GCSEs in maths and English language, which students will take for the first time in summer 2017.
The questions may not be in exactly the same style as the new GCSE questions, but they will be suitable for students who are studying for the GCSE. The questions used in the tests will not reflect any particular exam board’s style of GCSE questions. We will publish some example questions later this year.
Each test is divided into several booklets. Each student takes just one booklet, so no student has to take the whole test.
The reference test for maths, unlike the GCSE, will not be tiered. Most of the questions will be based on the GCSE content that all students taking the qualification have to study. There will be a few questions at the end of each booklet that will be based on the content only studied by those preparing to take the higher tier GCSE, but these will be clearly marked.
The reference test for English will include questions that require students to write longer responses as well others that can be answered with short, one sentence responses. There is no testing of spoken language in the English test.
The questions will remain largely the same from year to year to enable performance standards to be compared. Therefore, the questions used in the tests have to remain confidential and will not be published, and we will not be able to provide individual feedback to students.
The new tests will be developed over the next 18 months and the first national reference tests will be taken in March 2017. In future years the information from the tests will be taken into account when GCSEs are awarded.
In September and October 2015, we will trial questions that have been developed for the new tests. In March 2016, we will hold a preliminary reference test. This will involve the same number of schools and students as future tests but the purpose of the tests in 2016 is to confirm that all the operational arrangements are working well. The preliminary test is a very important step that will allow us to introduce the reference test smoothly in 2017.
Each year, a random sample of 30 students from around 300 schools will be asked to take a test booklet in maths or English. The students will be in year 11 and will take the test in early March, before they take their GCSEs (in late May and early June). The test will take each student around an hour to complete. The results will be analysed only at the national level; there will be no results for individual schools or students.
We would expect all randomly selected pupils to take the test. This will provide the most statistically sound sample. However, headteachers will have the option to exclude pupils from the testing - at their discretion and in exceptional circumstances. This option would be used for a specific reason such as accessibility or situations where taking the test may cause undue distress for the pupil. NFER, which is administering the tests on our behalf, will make the test as student-friendly as possible and allow all reasonable and appropriate access arrangements so it is hoped that such exclusions will be needed only in rare cases.
NFER, will select the schools to take part each year. We will use a different sample of schools each year. The selected schools will provide a list of all their students in year 11 that they expect will take the GCSE (in maths or English language). From this list NFER will randomly select which students will be asked to take the test.
NFER is working with us to develop the tests. In order to minimise the burden on participating schools, a test administrator from NFER will come to the school on a pre-agreed day and carry out the tests. The school will need to provide a room where the students can take the test and NFER’s administrator will invigilate it. The test will last around an hour for each student.
Towards the end of August each year we will publish the national measures of performance in the tests, around when exam boards publish GCSE results. We will also explain how performance in the national reference tests has been taken into account in awarding the GCSEs.
The results of the reference tests will set out the percentage of students in that year who are predicted, based on the tests, to achieve at least a grade 4, a grade 5 or a grade 7 in their GCSE. The tests will show if these percentages change from year to year. And it is these changes, if any, that will be taken into account when GCSEs are being awarded. We are developing the details of what we will publish and will confirm this later in the year. The first time that we will able to compare performance between years will be in 2018.
There will be no results for individual students or schools. So, for example, no result can be recorded in students’ school records. The only information will be for England as a whole.
Published: 1 June 2015
From: Ofqual
Part of: National Reference Test: information


Modify Profile Informations
Make sure to set all the fields you want to change
User image
Upload
Choose file
User Information
Email
Username
Name
Surname
Location


about this blog
Kia ora! My name is Benjamin, and I love mountains!
This blog is to showcase the various projects and tasks that I will be completing throughout my Bootcamp experience with Enspiral Dev Academy.
This Blog will is an ongoing project and will update week by week.
- sprint one - technical: The Story of Te Houtaewa
- sprint one - core: identity, values and strengths
- sprint two - technical: classes vs. ids
- sprint two - core: learning reflections
- sprint three - core: emotional intelligence
- sprint three - technical: JavaScript, the DOM, and DevTools
- sprint four - core: neuroplasticity and growth mindset
- sprint four - technical: problem solving


Safari IT Books Language Constructs Functional Programming Haskell Safari IT Books Programming Programming Programming Bryan O'Sullivan Donald Bruce Stewart John Goerzen O'Reilly Media, Inc. Real World Haskell, 1st Edition13.5. Extended Example: Numeric TypesWe've told you how powerful and expressive Haskell's type system
is. We've shown you a lot of ways to use that power. Here's a chance to
really see that in action. Back in Section 6.4.4,
we showed the numeric typeclasses that come with Haskell. Let's see what
we can do by defining new types and utilizing the numeric typeclasses to
integrate them with basic mathematics in Haskell. To begin let's think through what we'd like to see out of
ghci when we interact with our new
types. To start with, it might be nice to render numeric expressions as
strings, making sure to indicate proper precedence. Perhaps we could
create a function called prettyShow to
do that. We'll show you how to write it in a bit, but first we'll look at
how we might use it: ghci> :l num.hs
[1 of 1] Compiling Main ( num.hs, interpreted )
Ok, modules loaded: Main.
ghci> 5 + 1 * 3
8
ghci> prettyShow $ 5 + 1 * 3
"5+(1*3)"
ghci> prettyShow $ 5 * 1 + 3
"(5*1)+3" That looks nice, but it wasn't all that smart. We could
easily simplify out the 1 * part of the
expression. How about a function to do some very basic
simplification? ghci> prettyShow $ simplify $ 5 + 1 * 3
"5+3"
How about converting a numeric expression to Reverse Polish Notation (RPN)? RPN
is a postfix notation that never requires parentheses and is commonly
found on HP calculators. RPN is a stack-based notation. We push numbers
onto the stack, and when we enter operations, they pop the most recent
numbers off the stack and place the result on the stack: ghci> rpnShow $ 5 + 1 * 3
"5 1 3 * +"
ghci> rpnShow $ simplify $ 5 + 1 * 3
"5 3 +" Maybe it would be nice to be able to represent simple
expressions with symbols for the unknowns: ghci> prettyShow $ 5 + (Symbol "x") * 3
"5+(x*3)"
It's often important to track units of measure when
working with numbers. For instance, when you see the number 5, does it
mean 5 meters, 5 feet, or 5 bytes? Of course, if you divide 5 meters by 2
seconds, the system ought to be able to figure out the appropriate units.
Moreover, it should stop you from adding 2 seconds to 5 meters: ghci> 5 / 2
2.5
ghci> (units 5 "m") / (units 2 "s")
2.5_m/s
ghci> (units 5 "m") + (units 2 "s")
*** Exception: Mis-matched units in add or subtract
ghci> (units 5 "m") + (units 2 "m")
7_m
ghci> (units 5 "m") / 2
2.5_m
ghci> 10 * (units 5 "m") / (units 2 "s")
25.0_m/s If we define an expression or a function that is valid for
all numbers, we should be able to calculate the result, or render the
expression. For instance, if we define test to have type Num a
=> a—and, say, test = 2 * 5 +
3, then we ought to be able to do this: ghci> test
13
ghci> rpnShow test
"2 5 * 3 +"
ghci> prettyShow test
"(2*5)+3"
ghci> test + 5
18
ghci> prettyShow (test + 5)
"((2*5)+3)+5"
ghci> rpnShow (test + 5)
"2 5 * 3 + 5 +" Since we have units, we should be able to handle some
basic trigonometry as well. Many of these operations operate on angles.
Let's make sure that we can handle both degrees and radians: ghci> sin (pi / 2)
1.0
ghci> sin (units (pi / 2) "rad")
1.0_1.0
ghci> sin (units 90 "deg")
1.0_1.0
ghci> (units 50 "m") * sin (units 90 "deg")
50.0_m Finally, we ought to be able to put all this together and
combine different kinds of expressions: ghci> ((units 50 "m") * sin (units 90 "deg")) :: Units (SymbolicManip Double)
50.0*sin(((2.0*pi)*90.0)/360.0)_m
ghci> prettyShow $ dropUnits $ (units 50 "m") * sin (units 90 "deg")
"50.0*sin(((2.0*pi)*90.0)/360.0)"
ghci> rpnShow $ dropUnits $ (units 50 "m") * sin (units 90 "deg")
"50.0 2.0 pi * 90.0 * 360.0 / sin *"
ghci> (units (Symbol "x") "m") * sin (units 90 "deg")
x*sin(((2.0*pi)*90.0)/360.0)_m Everything you've just seen is possible with Haskell types
and classes. In fact, you've been reading a real ghci session demonstrating
num.hs, which you'll see shortly. 13.5.1. First StepsLet's think about how we would accomplish everything
just shown. To start with, we might use ghci to check the type of (+), which is Num a
=> a -> a -> a. If we want to make some custom
behavior for the plus operator possible, then we will have to define a
new type and make it an instance of Num. This type will need to store an
expression symbolically. We can start by thinking of operations such as
addition. To store that, we will need to store the operation itself, its
left and right sides. The left and right sides could themselves be
expressions. We can therefore think of an expression as a sort of
tree. Let's start with some simple types: -- file: ch13/numsimple.hs
-- The "operators" that we're going to support
data Op = Plus | Minus | Mul | Div | Pow
deriving (Eq, Show)
{- The core symbolic manipulation type -}
data SymbolicManip a =
Number a -- Simple number, such as 5
| Arith Op (SymbolicManip a) (SymbolicManip a)
deriving (Eq, Show)
{- SymbolicManip will be an instance of Num. Define how the Num
operations are handled over a SymbolicManip. This will implement things
like (+) for SymbolicManip. -}
instance Num a => Num (SymbolicManip a) where
a + b = Arith Plus a b
a - b = Arith Minus a b
a * b = Arith Mul a b
negate a = Arith Mul (Number (-1)) a
abs a = error "abs is unimplemented"
signum _ = error "signum is unimplemented"
fromInteger i = Number (fromInteger i)
First, we define a type called Op, which simply represents some of the
operations we will support. Next, there is a definition for SymbolicManip a. Because of the Num a constraint, any Num can be used for the a. So a full type may be something like
SymbolicManip Int. A SymbolicManip type
can be a plain number or some arithmetic operation. The type for the
Arith constructor is recursive, which
is perfectly legal in Haskell. Arith
creates a SymbolicManip out of an
Op and two other SymbolicManip items. Let's look at an example: ghci> :l numsimple.hs
[1 of 1] Compiling Main ( numsimple.hs, interpreted )
Ok, modules loaded: Main.
ghci> Number 5
Number 5
ghci> :t Number 5
Number 5 :: (Num t) => SymbolicManip t
ghci> :t Number (5::Int)
Number (5::Int) :: SymbolicManip Int
ghci> Number 5 * Number 10
Arith Mul (Number 5) (Number 10)
ghci> (5 * 10)::SymbolicManip Int
Arith Mul (Number 5) (Number 10)
ghci> (5 * 10 + 2)::SymbolicManip Int
Arith Plus (Arith Mul (Number 5) (Number 10)) (Number 2) You can see that we already have a very basic
representation of expressions working. Notice how Haskell "converted"
5 * 10 + 2 into a SymbolicManip, and even handled order of
evaluation properly. This wasn't really a true conversion; SymbolicManip is a first-class number now.
Integer numeric literals are internally treated as being wrapped in
fromInteger anyway, so 5 is just as valid as a SymbolicManip Int as it as an Int. From here, then, our task is simple: extend the SymbolicManip type to be able to represent all
the operations we will want to perform, implement instances of it for
the other numeric typeclasses, and implement our own instance of
Show for SymbolicManip that renders this tree in a more
accessible fashion. 13.5.2. Completed CodeHere is the completed num.hs, which
was used with the ghci examples at
the beginning of this chapter. Let's look at this code one piece at a
time: -- file: ch13/num.hs
import Data.List
--------------------------------------------------
-- Symbolic/units manipulation
--------------------------------------------------
-- The "operators" that we're going to support
data Op = Plus | Minus | Mul | Div | Pow
deriving (Eq, Show)
{- The core symbolic manipulation type. It can be a simple number,
a symbol, a binary arithmetic operation (such as +), or a unary
arithmetic operation (such as cos)
Notice the types of BinaryArith and UnaryArith: it's a recursive
type. So, we could represent a (+) over two SymbolicManips. -}
data SymbolicManip a =
Number a -- Simple number, such as 5
| Symbol String -- A symbol, such as x
| BinaryArith Op (SymbolicManip a) (SymbolicManip a)
| UnaryArith String (SymbolicManip a)
deriving (Eq)
In this section of code, we define an Op that is identical to the one we used
earlier. We also define SymbolicManip, which is similar to what we
used before. In this version, we now support unary arithmetic operations
(those which take only one parameter) such as abs or cos.
Next we define our instance of Num: -- file: ch13/num.hs
{- SymbolicManip will be an instance of Num. Define how the Num
operations are handled over a SymbolicManip. This will implement things
like (+) for SymbolicManip. -}
instance Num a => Num (SymbolicManip a) where
a + b = BinaryArith Plus a b
a - b = BinaryArith Minus a b
a * b = BinaryArith Mul a b
negate a = BinaryArith Mul (Number (-1)) a
abs a = UnaryArith "abs" a
signum _ = error "signum is unimplemented"
fromInteger i = Number (fromInteger i)
This is pretty straightforward and also similar to our
earlier code. Note that earlier we weren't able to properly support
abs, but now with the UnaryArith constructor, we can.  Now let's work on converting our
expressions to strings for display: -- file: ch13/num.hs
{- Show a SymbolicManip as a String, using conventional
algebraic notation -}
prettyShow :: (Show a, Num a) => SymbolicManip a -> String
-- Show a number or symbol as a bare number or serial
prettyShow (Number x) = show x
prettyShow (Symbol x) = x
prettyShow (BinaryArith op a b) =
let pa = simpleParen a
pb = simpleParen b
pop = op2str op
in pa ++ pop ++ pb
prettyShow (UnaryArith opstr a) =
opstr ++ "(" ++ show a ++ ")"
op2str :: Op -> String
op2str Plus = "+"
op2str Minus = "-"
op2str Mul = "*"
op2str Div = "/"
op2str Pow = "**"
{- Add parentheses where needed. This function is fairly conservative
and will add parenthesis when not needed in some cases.
Haskell will have already figured out precedence for us while building
up the SymbolicManip. -}
simpleParen :: (Show a, Num a) => SymbolicManip a -> String
simpleParen (Number x) = prettyShow (Number x)
simpleParen (Symbol x) = prettyShow (Symbol x)
simpleParen x@(BinaryArith _ _ _) = "(" ++ prettyShow x ++ ")"
simpleParen x@(UnaryArith _ _) = prettyShow x
{- Showing a SymbolicManip calls the prettyShow function on it -}
instance (Show a, Num a) => Show (SymbolicManip a) where
show a = prettyShow a
We start by defining a function prettyShow. It renders an expression using
conventional style. The algorithm is fairly simple: bare numbers and
symbols are rendered bare; binary arithmetic is rendered with the two
sides plus the operator in the middle, and, of course, we handle the
unary operators as well. op2str
simply converts an Op to a String. In simpleParen, we have a quite conservative
algorithm that adds parentheses to keep precedence clear in the result.
Finally, we make SymbolicManip an
instance of Show, using prettyShow to accomplish that. Now let's
implement an algorithm that converts an expression to a string in RPN
format: -- file: ch13/num.hs
{- Show a SymbolicManip using RPN. HP calculator users may
find this familiar. -}
rpnShow :: (Show a, Num a) => SymbolicManip a -> String
rpnShow i =
let toList (Number x) = [show x]
toList (Symbol x) = [x]
toList (BinaryArith op a b) = toList a ++ toList b ++
[op2str op]
toList (UnaryArith op a) = toList a ++ [op]
join :: [a] -> [[a]] -> [a]
join delim l = concat (intersperse delim l)
in join " " (toList i)
Fans of RPN will note how much simpler this algorithm
is compared to the algorithm used to render with conventional notation.
In particular, we didn't have to worry about where to add parentheses,
because RPN can, by definition, be evaluated only one way. Next, let's
see how we might implement a function to do some rudimentary
simplification on expressions: -- file: ch13/num.hs
{- Perform some basic algebraic simplifications on a SymbolicManip. -}
simplify :: (Num a) => SymbolicManip a -> SymbolicManip a
simplify (BinaryArith op ia ib) =
let sa = simplify ia
sb = simplify ib
in
case (op, sa, sb) of
(Mul, Number 1, b) -> b
(Mul, a, Number 1) -> a
(Mul, Number 0, b) -> Number 0
(Mul, a, Number 0) -> Number 0
(Div, a, Number 1) -> a
(Plus, a, Number 0) -> a
(Plus, Number 0, b) -> b
(Minus, a, Number 0) -> a
_ -> BinaryArith op sa sb
simplify (UnaryArith op a) = UnaryArith op (simplify a)
simplify x = x
This function is pretty simple. For certain binary
arithmetic operations—for instance, multiplying any value by 1—we are
able to easily simplify the situation. First, we obtain simplified
versions of both sides of the calculation (this is where recursion hits)
and then simplify the result. We have little to do with unary operators,
so we just simplify the expression they act upon. From here on, we will add support for units of measure
to our established library. This will let us represent quantities such
as "5 meters." We start, as before, by defining a type: -- file: ch13/num.hs
{- New data type: Units. A Units type contains a number
and a SymbolicManip, which represents the units of measure.
A simple label would be something like (Symbol "m") -}
data Num a => Units a = Units a (SymbolicManip a)
deriving (Eq)
So, Units contains a
number and a label that is itself a SymbolicManip. Next, it will probably come as
no surprise to see an instance of Num
for Units: -- file: ch13/num.hs
{- Implement Units for Num. We don't know how to convert between
arbitrary units, so we generate an error if we try to add numbers with
different units. For multiplication, generate the appropriate
new units. -}
instance (Num a) => Num (Units a) where
(Units xa ua) + (Units xb ub)
| ua == ub = Units (xa + xb) ua
| otherwise = error "Mis-matched units in add or subtract"
(Units xa ua) - (Units xb ub) = (Units xa ua) + (Units (xb * (-1)) ub)
(Units xa ua) * (Units xb ub) = Units (xa * xb) (ua * ub)
negate (Units xa ua) = Units (negate xa) ua
abs (Units xa ua) = Units (abs xa) ua
signum (Units xa _) = Units (signum xa) (Number 1)
fromInteger i = Units (fromInteger i) (Number 1)
Now it may be clear why we use a SymbolicManip instead of a String to store the unit of measure. As
calculations such as multiplication occur, the unit of measure also
changes. For instance, if we multiply 5 meters by 2 meters, we obtain 10
square meters. We force the units for addition to match and implement
subtraction in terms of addition.
Let's look at more typeclass instances for Units: -- file: ch13/num.hs
{- Make Units an instance of Fractional -}
instance (Fractional a) => Fractional (Units a) where
(Units xa ua) / (Units xb ub) = Units (xa / xb) (ua / ub)
recip a = 1 / a
fromRational r = Units (fromRational r) (Number 1)
{- Floating implementation for Units.
Use some intelligence for angle calculations: support deg and rad
-}
instance (Floating a) => Floating (Units a) where
pi = (Units pi (Number 1))
exp _ = error "exp not yet implemented in Units"
log _ = error "log not yet implemented in Units"
(Units xa ua) ** (Units xb ub)
| ub == Number 1 = Units (xa ** xb) (ua ** Number xb)
| otherwise = error "units for RHS of ** not supported"
sqrt (Units xa ua) = Units (sqrt xa) (sqrt ua)
sin (Units xa ua)
| ua == Symbol "rad" = Units (sin xa) (Number 1)
| ua == Symbol "deg" = Units (sin (deg2rad xa)) (Number 1)
| otherwise = error "Units for sin must be deg or rad"
cos (Units xa ua)
| ua == Symbol "rad" = Units (cos xa) (Number 1)
| ua == Symbol "deg" = Units (cos (deg2rad xa)) (Number 1)
| otherwise = error "Units for cos must be deg or rad"
tan (Units xa ua)
| ua == Symbol "rad" = Units (tan xa) (Number 1)
| ua == Symbol "deg" = Units (tan (deg2rad xa)) (Number 1)
| otherwise = error "Units for tan must be deg or rad"
asin (Units xa ua)
| ua == Number 1 = Units (rad2deg $ asin xa) (Symbol "deg")
| otherwise = error "Units for asin must be empty"
acos (Units xa ua)
| ua == Number 1 = Units (rad2deg $ acos xa) (Symbol "deg")
| otherwise = error "Units for acos must be empty"
atan (Units xa ua)
| ua == Number 1 = Units (rad2deg $ atan xa) (Symbol "deg")
| otherwise = error "Units for atan must be empty"
sinh = error "sinh not yet implemented in Units"
cosh = error "cosh not yet implemented in Units"
tanh = error "tanh not yet implemented in Units"
asinh = error "asinh not yet implemented in Units"
acosh = error "acosh not yet implemented in Units"
atanh = error "atanh not yet implemented in Units"
We didn't supply implementations for every function,
but quite a few have been defined. Now let's define a few utility
functions for working with units: -- file: ch13/num.hs
{- A simple function that takes a number and a String and returns an
appropriate Units type to represent the number and its unit of measure -}
units :: (Num z) => z -> String -> Units z
units a b = Units a (Symbol b)
{- Extract the number only out of a Units type -}
dropUnits :: (Num z) => Units z -> z
dropUnits (Units x _) = x
{- Utilities for the Unit implementation -}
deg2rad x = 2 * pi * x / 360
rad2deg x = 360 * x / (2 * pi)
First, we have units, which makes it easy to craft simple
expressions. It's faster to say units 5
"m" than Units 5 (Symbol
"m"). We also have a corresponding dropUnits, which discards the unit of measure
and returns the embedded bare Num.
Finally, we define some functions for use by our earlier instances to
convert between degrees and radians. Next, we just define a Show instance for Units: -- file: ch13/num.hs
{- Showing units: we show the numeric component, an underscore,
then the prettyShow version of the simplified units -}
instance (Show a, Num a) => Show (Units a) where
show (Units xa ua) = show xa ++ "_" ++ prettyShow (simplify ua)
That was simple. For one last piece, we define a
variable test to experiment
with: -- file: ch13/num.hs
test :: (Num a) => a
test = 2 * 5 + 3
So, looking back over all this code, we have done what
we set out to accomplish: implement more instances for SymbolicManip. We have also introduced another
type called Units, which stores a
number and a unit of measure. We employed several show-like functions,
which render the SymbolicManip or
Units in different ways. There is one other point that this example drives home:
every language—even those with objects and overloading—has parts that
are special in some way. In Haskell, the "special" bits are extremely
small. We just developed a new representation for something as
fundamental as a number, and it was really quite easy. Our new type is
first-class, and the compiler knows what functions to use with it at
compile time. Haskell takes code reuse and interchangeability to the
extreme. It is easy to make code generic and work on things of many
different types. It's also easy to create new types and
automatically make them first-class features of the system. Remember our ghci
examples at the beginning of the chapter? All of them were made with the
code in this example. You might want to try them out for yourself and
see how they work. |