Understading newtype and when to use it - purescript

I'm pretty new to purescript and functional programming
Newtypes are distinct from the point of view of the type system. This gives an extra layer of type safety.
This is what the opening ~50 lines of code look like for my Sudoku Solver (so far):
newtype Index = Index Int
newtype Option = Option Int
newtype Column = Column Int
newtype Row = Row Int
newtype Box = Box Int
newtype Cell = Cell Int
derive newtype instance eqIndex :: Eq Index
derive newtype instance eqOption :: Eq Option
derive newtype instance eqColumn :: Eq Column
derive newtype instance eqRow :: Eq Row
derive newtype instance eqBox :: Eq Box
derive newtype instance eqCell :: Eq Cell
derive newtype instance semiringIndex :: Semiring Index
derive newtype instance semiringOption :: Semiring Option
derive newtype instance semiringColumn :: Semiring Column
derive newtype instance semiringRow :: Semiring Row
derive newtype instance semiringBox :: Semiring Box
derive newtype instance semiringCell :: Semiring Cell
derive newtype instance ringIndex :: Ring Index
derive newtype instance ringOption :: Ring Option
derive newtype instance ringColumn :: Ring Column
derive newtype instance ringRow :: Ring Row
derive newtype instance ringBox :: Ring Box
derive newtype instance ringCell :: Ring Cell
derive newtype instance commutativeRingIndex :: CommutativeRing Index
derive newtype instance commutativeRingOption :: CommutativeRing Option
derive newtype instance commutativeRingColumn :: CommutativeRing Column
derive newtype instance commutativeRingRow :: CommutativeRing Row
derive newtype instance commutativeRingBox :: CommutativeRing Box
derive newtype instance commutativeRingCell :: CommutativeRing Cell
derive newtype instance euclideanRingIndex :: EuclideanRing Index
derive newtype instance euclideanRingOption :: EuclideanRing Option
derive newtype instance euclideanRingColumn :: EuclideanRing Column
derive newtype instance euclideanRingRow :: EuclideanRing Row
derive newtype instance euclideanRingBox :: EuclideanRing Box
derive newtype instance euclideanRingCell :: EuclideanRing Cell
derive newtype instance showIndex :: Show Index
derive newtype instance showOption :: Show Option
derive newtype instance showColumn :: Show Column
derive newtype instance showRow :: Show Row
derive newtype instance showBox :: Show Box
derive newtype instance showCell :: Show Cell
class IsInt a where toInt :: a -> Int
instance ciInt :: IsInt Int where toInt a = a
instance ciIndex :: IsInt Index where toInt (Index a) = a
instance ciOption :: IsInt Option where toInt (Option a) = a
instance ciColumn :: IsInt Column where toInt (Column a) = a
instance ciRow :: IsInt Row where toInt (Row a) = a
instance ciBox :: IsInt Box where toInt (Box a) = a
instance ciCell :: IsInt Cell where toInt (Cell a) = a
Likely, I'll soon need Ord and a few others as well.
So right now this stops me from from adding/subtracting an index and a row by accident, or mixing up parameters to functions that manipulate these types. It's also a pretty big amount of boilerplate in order to effectively have 7 names for Int.
I'm guessing this is overkill and I probably shouldn't bother? I could use type synonyms to keep the code looking descriptive and lose some type safety.
Is there another/better way?

As with everything in life, there is no recipe. It's a spectrum. No type safety at all on one extreme, or drown in newtypes on the other. The truth, as always, lies somewhere in the middle.
But to tell you anything more specific, I'd need to know the larger design. It's kind of impossible to say just from those definitions. It might be that you actually do need all of them, in which case... well... you do need all of them.
But in my experience, that's usually not the case. Some of the questions to ponder could be:
Do all of them really need Ring and CommutativeRing? Somehow I doubt that.
Do all of them really need to be separate, or could some be combined in one type? For example, Row and Column could be a Point together.
If you want your type to have all characteristics of a number, then perhaps it should actually be a number?
Could the type safety be achieved by using named parameters (via records) instead?
The way I usually go about these things is to start barebones and then add support for stuff as need arises. You will probably be surprised how little need will arise in practice.
And this point applies to using newtypes in the first place. Is it really that probable that you might mix up rows and columns? Or are you just trying to plug every possible hole just in case? If it's the latter, then yes, you do end up with a ton of hole plugs, there's no surprise there.

Related

How declare tagged union of polymorphic collection types

I'm new to Purescript. My current learning exercise is to create a tagged union of polymorphic Array and List. I'll use it in a function that finds the length of any Array or List. Here's my attempt:
import Data.List as L
import Data.Array as A
data Collection = CollectionList (forall a. L.List a)
| CollectionArray (forall b. Array b)
colLength :: Collection -> Int
colLength (CollectionList list) = L.length list
colLength (CollectionArray arr) = A.length arr
main :: Effect Unit
main = do
logShow (colLength (CollectionArray [3,5]))
The compiler doesn't like it:
Could not match type Int with type b0
while checking that type Int is at least as general as type b0
while checking that expression 3 has type b0
in value declaration main
where b0 is a rigid type variable
I'm confused by the parts, checking that type Int is at least as general as type b0 and b0 is a rigid type variable. My intention was to allow b to be anything. Not sure what I did to make the compiler put conditions on what b can be.
If you know how, please show the correct way to define a tagged union of polymoric types that'll work in my colLength function.
forall a doesn't mean "any type goes here"
It means that whoever accesses the value, gets to choose what a is, and whoever provides the value has to make sure that the value is of that type. It's a contract between the provider and the consumer.
So when you provide the value CollectionArray [3,5], you have to make it such that it works for all possible a that whoever accesses that value later might choose.
Obviously, there is only one way you can construct such value:
CollectionArray []
What you probably actually meant to do (and I'm guessing here) was to make your collection polymorphic, in the sense that it can contain values of any type, but the type is chosen by whoever creates the collection, and then whoever accesses it later has to deal with that particular type.
To do that, you have to put the type variable on the outside:
data Collection a = CollectionList (L.List a)
| CollectionArray (Array a)
That way, when you create a collection CollectionArray [3,5], it becomes of type Collection Int, and now everywhere you pass it, such as colLength, will have to deal with that Int
This, in turn, can be achieved by making colLength itself generic:
colLength :: forall a. Collection a -> Int
colLength (CollectionList list) = L.length list
colLength (CollectionArray arr) = A.length arr
Now whoever accesses (i.e. calls) colLength itself gets to choose what a is, which works fine, because it's the same place that created the Connection Int in the first place.

Get a newtype'd records underlying type in purescript

I'm trying to see if there's an easy way to get the type of a newtype'd record to put in function signatures.
newtype T1 = T1 { foo:: Int}
derive instance newtypeT1 :: Newtype T1 _
... other classes that require me to newtype the record ...
I know I can access a records members with
_.property and I can compose that with unwrap
unwrap >>> _.property to get a function for that property, but I'd like to write a function similar to
testFoo :: forall a. (_ -> a) -> Effect a
testFoo accessor = (unwrap >>> accessor) <$> loadT1
This works but the wildcard symbol gives an warning, but I'm not sure how to get that record definition from T1. (This is a minimal example, I have a massive property object that is from an external source.
A workaround I've been using for now has been to declare my type like
type InnerT1 = { foo ::Int}
newtype T1 = T1 InnerT1
and exporting that InnerT1 so it can be used in my test file, but this seems a bit clunky and I am wondering if there is a better way?
You can use the Newtype class to get at the inner type:
testFoo :: forall a inner. Newtype T1 inner => (inner -> a) -> Effect a
testFoo accessor = (unwrap >>> accessor) <$> loadT1
This works without additional annotations, because the class has a functional dependency Newtype a b | a -> b, which means that the inner type is uniquely determined by the outer type.

How to use instance chain to solve overlapping

I've been trying next purescript ver 0.12.0-rc1.
I have a question how to use new feature 'instance chain'.
in my understanding the instance chain provides a feature to be able to specify instance resolving order explicitly. this solves avoid instance definition overlapping.
so I suppose it may work:
class A a
class B b
class C c where
c :: c -> String
instance ca :: A a => C a where
c = const "ca"
else
instance cb :: B b => C b where
c = const "cb"
data X = X
instance bx :: B X
main :: forall eff. Eff (console :: CONSOLE | eff) Unit
main = logShow $ c X
but cannot compiled.
what is not correctly?
or what is the instance chain usage?
result:
Error found:
in module Main
at src/Main.purs line 23, column 8 - line 23, column 20
No type class instance was found for
Main.A X
while applying a function c
of type C t0 => t0 -> String
to argument X
while inferring the type of c X
in value declaration main
where t0 is an unknown type
Even with instance chains matching is still done on the head of an instance. There is no "backtracking" when any of constraint fails for the chosen instance.
Your instances are completely overlapping on the head, so your first instance always matches before second one and it fails because there is no A instance for X.
Instance chains allows you to define explicit ordering of instance resolution without relying on for example alphabetical ordering of names etc. (as it was done till 0.12.0 version - please check the third paragraph here). For example you can define this overlapping scenario:
class IsRecord a where
isRecord :: a -> Boolean
instance a_isRecordRecord :: IsRecord (Record a) where
isRecord _ = true
instance b_isRecordOther :: IsRecord a where
isRecord _ = false
as
instance isRecordRecord :: IsRecord (Record a) where
isRecord _ = true
else instance isRecordOther :: IsRecord a where
isRecord _ = false
I hope it compiles - I don't have purs-0.12.0-rc yet ;-)

How to create Functor instance for Type composed of Either and Maybe

I'm having trouble with a Functor instance for a type which is basically just nested Either and Maybe.
data Tuple a b = Tuple a b
data Primitive = String String | Boolean Boolean | Number Number | Null
data JsonValue = Object (Map String JsonValue) | Array (List JsonValue) | Primitive
type Path = List String
data JsonGraphValue = JsonGraphObject (Map String JsonGraphValue) | Atom JsonValue | Ref Path | Error JsonValue | JsonPrimitive Primitive
newtype JsonGraphRecResult a = JsonGraphRecResult (Either String (Tuple (Maybe a) (List Path)))
instance jsonGraphRecResultFunctor :: Functor JsonGraphRecResult where
map f (JsonGraphRecResult (Right (Tuple (Just value) paths))) = JsonGraphRecResult (Right (Tuple (Just (f value)) paths))
map f value = value
I get the following error pointing to the "value" word at the end of the code above.
Could not match type
a1
with type
b0
while trying to match type JsonGraphRecResult a1
with type JsonGraphRecResult b0
while checking that expression value
has type JsonGraphRecResult b0
in value declaration jsonGraphRecResultFunctor
where b0 is a rigid type variable
a1 is a rigid type variable
It's not clear to me why JsonGraphRecResult is any different from the following Blah type which compiles fine:
newtype Blah a = Blah (Maybe a)
instance blahFunctor :: Functor Blah where
map f (Blah (Just x)) = Blah (Just (f x))
map f value = value
The following gist can be pasted directly into the "Try PureScript" online REPL in order to replicate the error.
Figured out the problem. I can't simply return the input value for the map function in the event that Either is Left, because the input value is not the right type. Here's a simplified version of the problem.
-- This is wrong because value is a Functor a, whereas map must return Functor b
map value#Nothing f = value
-- This is right, because even though both sides are Nothing, the right-hand side is a Maybe b vs. Maybe a
map Nothing f = Nothing
You should be able to just derive Functor instance for your type. But before you derive an instance for your final type you should derive it for your Tuple type (or just use Tuple from Data.Tuple ;-)):
data Tuple a b = Tuple a b
derive instance functorTuple :: Functor (Tuple a)
As you can see Functor instance can be defined only for types of kind * -> * so in this case we can map over type which occupies "last position" in Tuple.
In order to derive an instance for your JsonGraphRecResult you have to change order of types in it's internal Tuple to fulfill "last position" requirement:
newtype JsonGraphRecResult a =
JsonGraphRecResult (Either String (Tuple (List Path) (Maybe a)))
derive instance functorJsonGraphRecResult :: Functor JsonGraphRecResult
Here is relevant interactive snippet on trypurescript.org so you can play with this implementation.
There are more type classes for which you can use this deriving mechanism: Ord, Eq, Generic, Newtype...
It is also worth to point out in this context that in Purescript you have additional deriving option which is "newtype deriving". It's syntax is a bit different because it contains newtype keyword after derive - for example:
derive newtype instance someClassMyType :: SomeClass MyType
Newtype deriving is used for newtype "wrappers" for deriving instances of classes which given newtype internal type has already defined instances. In other words when you have newtype T = T a you can derive newtype instances for T for every class which a has instance of.
There is also another strategy - you can also use generic implementations of methods defined for some type classes in Data.Generic.Rep. These implementations can be used for types which are instances of Generic class... but this is whole another story ;-)
You can find more information about deriving in Phil's "24 Days of Purescript" series:
https://github.com/paf31/24-days-of-purescript-2016

Gentle Intro to Haskell: " .... there is no single type that contains both 2 and 'b'." Can I not make such a type ?

I am currently learning Haskell, so here are a beginner's questions:
What is meant by single type in the text below ?
Is single type a special Haskell term ? Does it mean atomic type here ?
Or does it mean that I can never make a list in Haskell in which I can put both 1 and 'c' ?
I was thinking that a type is a set of values.
So I cannot define a type that contains Chars and Ints ?
What about algebraic data types ?
Something like: data IntOrChar = In Int | Ch Char ? (I guess that should work but I am confused what the author meant by that sentence.)
Btw, is that the only way to make a list in Haskell in which I can put both Ints and Chars? Or is there a more tricky way ?
A Scala analogy: in Scala it would be possible to write implicit conversions to a type that represents both Ints and Chars (like IntOrChar) and then it would be possible to put seemlessly Ints and Chars into List[IntOrChar], is that not possible with Haskell ? Do I always have to explicitly wrap every Int or Char into IntOrChar if I want to put them into a list of IntOrChar ?
From Gentle Intro to Haskell:
Haskell also incorporates polymorphic types---types that are
universally quantified in some way over all types. Polymorphic type
expressions essentially describe families of types. For example,
(forall a)[a] is the family of types consisting of, for every type a,
the type of lists of a. Lists of integers (e.g. [1,2,3]), lists of
characters (['a','b','c']), even lists of lists of integers, etc., are
all members of this family. (Note, however, that [2,'b'] is not a
valid example, since there is no single type that contains both 2 and
'b'.)
Short answer.
In Haskell there are no implicit conversions. Also there are no union types - only disjoint unions(which are algebraic data types). So you can only write:
someList :: [IntOrChar]
someList = [In 1, Ch 'c']
Longer and certainly not gentle answer.
Note: This is a technique that's very rarely used. If you need it you're probably overcomplicating your API.
There are however existential types.
{-# LANGUAGE ExistentialQuantification, RankNTypes #-}
class IntOrChar a where
intOrChar :: a -> Either Int Char
instance IntOrChar Int where
intOrChar = Left
instance IntOrChar Char where
intOrChar = Right
data List = Nil
| forall a. (IntOrChar a) => Cons a List
someList :: List
someList = (1 :: Int) `Cons` ('c' `Cons` Nil)
Here I have created a typeclass IntOrChar with only function intOrChar. This way you can convert anything of type forall a. (IntOrChar a) => a to Either Int Char.
And also a special kind of list that uses existential type in its second constructor.
Here type variable a is bound(with forall) at the constructor scope. Therefore every time
you use Cons you can pass anything of type forall a. (IntOrChar a) => a as a first argument. Consequently during a destruction(i.e. pattern matching) the first argument will
still be forall a. (IntOrChar a) => a. The only thing you can do with it is either pass it on or call intOrChar on it and convert it to Either Int Char.
withHead :: (forall a. (IntOrChar a) => a -> b) -> List -> Maybe b
withHead f Nil = Nothing
withHead f (Cons x _) = Just (f x)
intOrCharToString :: (IntOrChar a) => a -> String
intOrCharToString x =
case intOrChar of
Left i -> show i
Right c -> show c
someListHeadString :: Maybe String
someListHeadString = withHead intOrCharToString someList
Again note that you cannot write
{- Wont compile
safeHead :: IntOrChar a => List -> Maybe a
safeHead Nil = Nothing
safeHead (Cons x _) = Just x
-}
-- This will
safeHead2 :: List -> Maybe (Either Int Char)
safeHead2 Nil = Nothing
safeHead2 (Cons x _) = Just (intOrChar x)
safeHead will not work because you want a type of IntOrChar a => Maybe a with a bound at safeHead scope and Just x will have a type of IntOrChar a1 => Maybe a1 with a1 bound at Cons scope.
In Scala there are types that include both Int and Char such as AnyVal and Any, which are both supertypes of Char and Int. In Haskell there is no such hierarchy, and all the basic types are disjoint.
You can create your own union types which describe the concept of 'either an Int or a Char (or you could use the built-in Either type), but there are no implicit conversions in Haskell to transparently convert an Int into an IntOrChar.
You could emulate the concept of 'Any' using existential types:
data AnyBox = forall a. (Show a, Hashable a) => AB a
heteroList :: [AnyBox]
heteroList = [AB (1::Int), AB 'b']
showWithHash :: AnyBox -> String
showWithHash (AB v) = show v ++ " - " ++ (show . hash) v
let strs = map showWithHash heteroList
Be aware that this pattern is discouraged however.
I think that the distinction that is being made here is that your algebraic data type IntOrChar is a "tagged union" - that is, when you have a value of type IntOrChar you will know if it is an Int or a Char.
By comparison consider this anonymous union definition (in C):
typedef union { char c; int i; } intorchar;
If you are given a value of type intorchar you don't know (apriori) which selector is valid. That's why most of the time the union constructor is used in conjunction with a struct to form a tagged-union construction:
typedef struct {
int tag;
union { char c; int i; } intorchar_u
} IntOrChar;
Here the tag field encodes which selector of the union is valid.
The other major use of the union constructor is to overlay two structures to get an efficient mapping between sub-structures. For example, this union is one way to efficiently access the individual bytes of a int (assuming 8-bit chars and 32-bit ints):
union { char b[4]; int i }
Now, to illustrate the main difference between "tagged unions" and "anonymous unions" consider how you go about defining a function on these types.
To define a function on an IntOrChar value (the tagged union) I claim you need to supply two functions - one which takes an Int (in the case that the value is an Int) and one which takes a Char (in case the value is a Char). Since the value is tagged with its type, it knows which of the two functions it should use.
If we let F(a,b) denote the set of functions from type a to type b, we have:
F(IntOrChar,b) = F(Int,b) \times F(Char,b)
where \times denotes the cross product.
As for the anonymous union intorchar, since a value doesn't encode anything bout its type the only functions which can be applied are those which are valid for both Int and Char values, i.e.:
F(intorchar,b) = F(Int,b) \cap F(Char,b)
where \cap denotes intersection.
In Haskell there is only one function (to my knowledge) which can be applied to both integers and chars, namely the identity function. So there's not much you could do with a list like [2, 'b'] in Haskell. In other languages this intersection may not be empty, and then constructions like this make more sense.
To summarize, you can have integers and characters in the same list if you create a tagged-union, and in that case you have to tag each of the values which will make you list look like:
[ I 2, C 'b', ... ]
If you don't tag your values then you are creating something akin to an anonymous union, but since there aren't any (useful) functions which can be applied to both integers and chars there's not really anything you can do with that kind of union.