Haskell compile time checking of smart constructors - haskell

I'm learning Haskell, running through the lectures:
http://www.cis.upenn.edu/~cis194/spring13/
I've got:
module HanoiDisk(HanoiDisk, hanoiDisk) where
import Control.Exception
data HanoiDisk = HanoiDisk' Integer deriving (Show)
hanoiDisk :: Integer -> HanoiDisk
hanoiDisk n = assert (n >= 1) $ HanoiDisk' n
This works, but if i have:
main = do
print(show (hanoiDisk (-3))
I only get an error during run-time and not at compile-time.
I'm pretty keen to understand how to eliminate run-time exceptions entirely.
Can anyone provide an alternative approach?
Thanks

Haskell checks types when compiling a code, not values. To make types depend on values is the job of "dependent types". It is an advanced topic.
The other way to achieve this is to make your hanoiDisk work not with Integers, but with some "PositiveInteger" type which can not possibly be negative (or 0 as well..?). It is a more basic approach.
There will be nothing to assert -- it should be impossible for you to even write down a negative value with this type. You'll have to make this type an instance of Num, Eq, Ord, and Show (maybe Enum as well).
The usual way is to define
data Nat = Z | S Nat
deriving (Eq, Show)

From what I understand, you want a way to "fail nicely" when someone applies the function hanoiDisk to an argument that's less than 1.
As a commenter stated, doing that at compile time is outside the scope of basic Haskell and you shouldn't need it in your day-to-day code!
You can definitely "fail nicely" by using the Either a b datatype.
The idea is that if you have a function hanoiDisk :: Integer -> HanoiDisk that takes an Integer and is supposed to return a HanoiDisk value if the input is "good" and an error value of some sort when the input is "bad", you can encode that using alternate constructors.
The constructors for the Either a b datatype are Left a and Right b where
an error output would be of the form Left a and a good output would be of the form Right b. Let's rewrite your function using this.
hanoiDisk :: Integer -> Either String HanoiDisk
hanoiDisk n = if n >= 1
then Right (HanoiDisk' n)
else Left "a hanoi disk must be least 1"
(Probably) More Appropriate Answer
Let's discuss the simpler problem of constructing numbers that must be nonnegative (as opposed to positive) in a way that's acceptable to the compiler.
I think the problem is tied to the way numbers are parsed by the compiler. Any time you use the symbols '0', '1', '2', '3', '4', ..., '9' to represent digits in your program the language parser expects the end result to conform to a type like Int, Double, etc. and so when you use these symbols you open yourself up to the possibility that someone might prepend a '-' to the sequence of digits and turn your nonnegative number into a negative one.
Let's make a new module called Natural which will allow us to create positive numbers. In it, we define "aliases" for the symbols '0',...,'1' using the first two letters of each symbol's name (eg. tw for '2'). Since humans write natural numbers using the decimal system, we create a data type called Natural that takes two arguments - the first digit of the number we're representing and then a list of subsequent digits. Finally, we selectively export functions from the module to prohibit "misuse" by users.
module Natural (ze,on,tw,th,fo,fi,si,se,ei,ni,Natural(..)) where
newtype Digit = Digit Int
ze = Digit 0
on = Digit 1
tw = Digit 2
th = Digit 3
fo = Digit 4
fi = Digit 5
si = Digit 6
se = Digit 7
ei = Digit 8
ni = Digit 9
data Natural = Nat Digit [Digit]
As an example, the natural number 312 would be represented as Nat th [on,tw].
Any module importing Natural would only have access to the functions that we export, so attempts to use anything else to define a value of type Natural would result in compile errors. Furthermore, since we didn't export the Digit constructor there's no way for importers to define their own values for the Digit type.
I'm leaving out definitions of the instances for Num, Integral, Eq, Ord, etc. because I don't think they would add more to my explanation.

Related

Does a type Int in the range of 1 <= x <= 255 exist? [duplicate]

This question already has answers here:
Haskell Type Level Constraint
(2 answers)
Closed 2 months ago.
I would like to create the following declaration:
data Color = B | W deriving Read
type Cell = (Color, Int) where 1 <= Int <= 255
Is there any solution to this problem ? The type, as it is, accepts any Int but those outside that range shouldn't compile.
Not quite, but if you import Data.Word, there is the type Word8.
This is an "8-bit unsigned integer type", so its values range from 0-255, rather than 1-255 as you wished.
If that is not suitable for you, then the techniques in the answers to the question Noughtmare referred to in their comment are additional things to consider.
1-to-255 is a pretty unusual range. Fairly sure this doesn't exist as such in any library. Are you sure you don't want 0-to-255? In that case, Word8 can be used.
For arbitrary upper bounds (but always 0 as the lower), there is Finite from the finite-typelis package.
In general, what you would typically do to express constraints on a type's values is to introduce a custom newtype wrapper. In doubt, I would actually select Int as the number type – it's way overkill in terms of bit count, but there's not much to be won by selecting something smaller because Haskell boxed values anyway involve an Int-sized pointer. Meanwhile it's still more efficient than Integer.
So,
newtype CellI = CellI { getCellI :: Int -- ^ In range [1,255]
}
mkCellI :: Int -> Maybe CellI
mkCellI n
| n>=1, n<=255 = Just (CellI n)
| otherwise = Nothing
Of course, such a wrapper is in some ways awkward. An alternative is to not express the invariant in Haskell at all, but simply use an ordinary Int and then Liquid Haskell for expressing the exact range. This is much more concise for a constraint like range-of-int than if you mess about with pseudo-dependent Haskell.

How to define all musical note names .. Cbb Cb C Cs Css .. as constructors in Haskell

I was playing with musical note names having the goal to not confuse enharmonic equals, i.e. I wanted to get the accidentals (sharps and flats) right. The note a perfect fifth above the note B needs to be Fs and not Gb, even though Fs and Gb are the same key on a piano keyboard.
Also I wanted the convenience of writing e.e. Fs in a haskell program, without spaces, quotes or an extra function.
I ended up defining 35 constructors, ranging from Cbb to Bss. While this worked and did get the accidentals right, I was unhappy about the limitation to at most two accidentals. Internally, the accidentals we represented asInts anyways.
Is there a way to define an infinite number of constructors as indicated in the title, so notes with any number of accidentals (like Cbbbb) could be used? Template haskell maybe?
Or alternatively, can I get the convenience of writing Cbbbb in a haskell program (without quotes, spaces or an extra function) without making Cbbbb a constructor?
I agree with Carsten that actually having lots of disperate constructors like that is a bad idea. It's much more sensible to use data like
data BaseNote = C | D | E | F | G | A | B
data PitchClass = PitchClass
{ baseNote :: BaseNote
, accidentals :: Int }
data Note = Note
{ pitchClass :: PitchClass
, octave :: Int }
As for
Also I wanted the convenience of writing e.e. Fs in a haskell program, without spaces, quotes or an extra function.
you have multiple options.
You could use -XPatternSynonyms. This lets you procure matchable constructors for already-defined data types.
{-# LANGUAGE PatternSynonyms #-}
pattern Cn = PitchClass C 0
pattern Cs = PitchClass C 1
pattern Cb = PitchClass C (-1)
...
These can be provided by a TemplateHaskell macro to avoid code duplication.
You could provide a function that makes it look as compact as single constructor names, but actually isn't.
(♮), (♯), (♭) :: BaseNote -> Int -> Note
bn♮octv = Note (PitchClass bn 0) octv
bn♯octv = Note (PitchClass bn 1) octv
bn♭octv = Note (PitchClass bn (-1)) octv
Now you can write things like
[A♮2, A♮2, C♯3, C♯3, D♮3, D♮3, C♯3]
TBH I don't think either of these is really good though. IMO it makes more sense to specify musical material not in absolute pitches at all, but rather as a sequence of either scale degrees or interval steps.

Haskell what does the ' symbol do?

As the title states, I see pieces of code online where the variables/functions have ' next to it, what does this do/mean?
ex:
function :: [a] -> [a]
function ...
function' :: ....
The notation comes from mathematics. It is read x prime. In pretty much any math manual you can find something like let x be a number and x' be the projection of ... (math stuff).
Why not using another convention? well, in mathematics It makes a lot of sense because It can be very pedagogical... In programming we aren't used to this convention so I don't see the point of using it, but I am not against it neither.
Just to give you an example of its use in mathematics so you can understand why It is used in Haskell. Below, the same triangle concept but one using prime convention and other not using it. It is pretty clear in the first picture that pairs (A, A'), (B, B'), ... are related by one being the vertex and the prime version being the midpoint of the oposite edge. Whereas in the second example, you just have to remember that A is the midpoint of the oposite edge of vertex P. First is easier and more pedagogical:
As the other answers said, function' is just another variable name. So,
don'tUse :: Int -> IO ()
don'tUse won'tBe''used'' = return ()
is just like
dontUse :: Int -> IO ()
dontUse wontBeUsed = return ()
with slightly different names. The only requirement is that the name starts with a lowercase-letter or underscore, after that you can have as many single-quote characters as you want.
Prelude> let _' = 1
Prelude> let _'' = 2
Prelude> let _''''''''' = 9
Prelude> _' + _'' * _'''''''''
19
...Of course it's not necessarily a good idea to name variables like that; normally such prime-names are used when making a slightly different version of an already named thing. For example, foldl and foldl' are functions with the same signature that do essentially the same thing, only with different strictness (which often affects performance memory usage and whether infinite inputs are allowed, but not the actual results).
That said, to the question
Haskell what does the ' symbol do?
– the ' symbol does in fact do various other things as well, but only when it appears not as a non-leading character in a name.
'a' is a character literal.
'Foo is a constructor used on the type level. See DataKinds.
'bar and ''Baz are quoted names. See TemplateHaskell.

ADT - constructor and type -- function accepting only one of the constructors

I wrote the hsexif library and I would now add a feature, but I'm not sure how to prepare the API.
I have the ExifValue type. A ExifValue can be among others a ExifRational, which has a numerator and a denominator. Often you want to display that value (show) as "num/den", for instance for an exposition time of 1/160.
However sometimes you want to show it as a floating-point number, for instance for the exposure compensation, which you would display as "-0.75" for instance, or the aperture ("6.3").
So I want to add a function:
formatAsFloatingPoint :: ExifValue -> Int -> String
The function takes the exif value and the number of floating points after the comma to output in the result string, and returns the formatted string.
However the function will then accept any ExifValue and the user will get a runtime error and no compile time warning if it gives a ExifText as a parameter to that function...
How would I go to make a clean and type-safe API in this case?
You need to think about how you expect this to be used.
The caller might always know they have an ExifRational and will only call formatAsFloatingPoint with such a value. In that case it would make sense to refactor your datatype:
data Rational = Rational !Int !Int
data ExifValue = ... | ExifRational Rational | ...
(or perhaps reuse some existing type for expressing rationals)
and then make formatAsFloatingPoint take a Rational:
formatAsFloatingPoint :: Rational -> Int -> String
This moves the responsibility to the caller to decide when to call it.
Alternatively, perhaps callers just want to display an arbitrary ExifValue, but with special behaviour if the value happens to be an ExifRational. In that case, just use a catch-all case, e.g.:
formatAsFloatingPoint :: ExifValue -> Int -> String
formatAsFloatingPoint n (ExifRational num den) = ...
formatAsFloatingPoint _ v = show v
There are more complicated approaches based on using a type parameter to flag what kind of thing you have, but that would involve refactoring the entire library and there's little evidence that's warranted here. If you have a more general problem across the codebase of wanting to signal that you have specific kinds of ExifValue, they might make sense.

Behavior of `=` in alloy fact

I was experimenting with alloy and wrote this code.
one sig s1{
vals: some Int
}{
#vals = 4
}
one sig s2{
vals: some Int
}{
#vals = 4
}
fact {
all a : s1.vals | a > 2
all i : s2.vals | i < 15
s1.vals = s2.vals
}
pred p{}
run p
It seems to me that {3,4,5,6} at least is a solution however Alloy says no instance found. When I comment s1.vals = s2.vals or change i < 15 to i > 2, it finds instances.
Can anyone please explain me why? Thanks.
Alloy's relationship with integers is sometimes mildly strained; it's not designed for heavily numeric applications, and many uses of integers in conventional programming are better handled in Alloy by other signatures.
The default bit width for integers is 4 bits, and Alloy uses twos-complement integers, so your run p is asking for a world in which integers range in value from -8 to 7. In that world, the constraint i < 15 is subject to integer overflow, and turns out to mean, in effect, i < -1. (To see this, comment out both of your constraints so that you get some instances. Then (a) leaf through the instances produced by the Analylzer and look at the integers that appear in them; you'll see their range is as I describe. Also, (b) open the Evaluator and type the numeral "15"; you'll see that its value in this universe is -1.)
If you change your run command to provide an appropriate bit width for integers (e.g. run p for 5 int), you'll get instances which are probably more like what you were expecting.
An alternative change, however, which leads to a more idiomatic Alloy model, is to abstract away from the specific kind of value by defining a sig for values:
sig value {}
Then change the declaration for vals in s1 and s2 from some Int to some value, and comment out the numeric constraints on them (or substitute some other interesting constraints for them). And then run p in a suitable scope (e.g. run p for 8 value).

Resources