Similar record types in a list/array in purescript - purescript

Is there any way to do something like
first = {x:0}
second = {x:1,y:1}
both = [first, second]
such that both is inferred as {x::Int | r} or something like that?
I've tried a few things:
[{x:3}] :: Array(forall r. {x::Int|r}) -- nope
test = Nil :: List(forall r. {x::Int|r})
{x:1} : test -- nope
type X r = {x::Int | r}
test = Nil :: List(X) -- nope
test = Nil :: List(X())
{x:1} : test
{x:1, y:1} : test -- nope
Everything I can think of seems to tell me that combining records like this into a collection is not supported. Kind of like, a function can be polymorphic but a list cannot. Is that the correct interpretation? It reminds me a bit of the F# "value restriction" problem, though I thought that was just because of CLR restrictions whereas JS should not have that issue. But maybe it's unrelated.
Is there any way to declare the list/array to support this?

What you're looking for is "existential types", and PureScript just doesn't support those at the syntax level the way Haskell does. But you can roll your own :-)
One way to go is "data abstraction" - i.e. encode the data in terms of operations you'll want to perform on it. For example, let's say you'll want to get the value of x out of them at some point. In that case, make an array of these:
type RecordRep = Unit -> Int
toRecordRep :: forall r. { x :: Int | r } -> RecordRep
toRecordRep {x} _ = x
-- Construct the array using `toRecordRep`
test :: Array RecordRep
test = [ toRecordRep {x:1}, toRecordRep {x:1, y:1} ]
-- Later use the operation
allTheXs :: Array Int
allTheXs = test <#> \r -> r unit
If you have multiple such operations, you can always make a record of them:
type RecordRep =
{ getX :: Unit -> Int
, show :: Unit -> String
, toJavaScript :: Unit -> Foreign.Object
}
toRecordRep r =
{ getX: const r.x
, show: const $ show r.x
, toJavaScript: const $ unsafeCoerce r
}
(note the Unit arguments in every function - they're there for the laziness, assuming each operation could be expensive)
But if you really need the type machinery, you can do what I call "poor man's existential type". If you look closely, existential types are nothing more than "deferred" type checks - deferred to the point where you'll need to see the type. And what's a mechanism to defer something in an ML language? That's right - a function! :-)
newtype RecordRep = RecordRep (forall a. (forall r. {x::Int|r} -> a) -> a)
toRecordRep :: forall r. {x::Int|r} -> RecordRep
toRecordRep r = RecordRep \f -> f r
test :: Array RecordRep
test = [toRecordRep {x:1}, toRecordRep {x:1, y:1}]
allTheXs = test <#> \(RecordRep r) -> r _.x
The way this works is that RecordRep wraps a function, which takes another function, which is polymorphic in r - that is, if you're looking at a RecordRep, you must be prepared to give it a function that can work with any r. toRecordRep wraps the record in such a way that its precise type is not visible on the outside, but it will be used to instantiate the generic function, which you will eventually provide. In my example such function is _.x.
Note, however, that herein lies the problem: the row r is literally not known when you get to work with an element of the array, so you can't do anything with it. Like, at all. All you can do is get the x field, because its existence is hardcoded in the signatures, but besides the x - you just don't know. And that's by design: if you want to put anything into the array, you must be prepared to get anything out of it.
Now, if you do want to do something with the values after all, you'll have to explain that by constraining r, for example:
newtype RecordRep = RecordRep (forall a. (forall r. Show {x::Int|r} => {x::Int|r} -> a) -> a)
toRecordRep :: forall r. Show {x::Int|r} => {x::Int|r} -> RecordRep
toRecordRep r = RecordRep \f -> f r
test :: Array RecordRep
test = [toRecordRep {x:1}, toRecordRep {x:1, y:1}]
showAll = test <#> \(RecordRep r) -> r show
Passing the show function like this works, because we have constrained the row r in such a way that Show {x::Int|r} must exist, and therefore, applying show to {x::Int|r} must work. Repeat for your own type classes as needed.
And here's the interesting part: since type classes are implemented as dictionaries of functions, the two options described above are actually equivalent - in both cases you end up passing around a dictionary of functions, only in the first case it's explicit, but in the second case the compiler does it for you.
Incidentally, this is how Haskell language support for this works as well.

Folloing #FyodorSoikin answer based on "existential types" and what we can find in purescript-exists we can provide yet another solution.
Finally we will be able to build an Array of records which will be "isomorphic" to:
exists tail. Array { x :: Int | tail }
Let's start with type constructor which can be used to existentially quantify over a row type (type of kind #Type). We are not able to use Exists from purescript-exists here because PureScript has no kind polymorphism and original Exists is parameterized over Type.
newtype Exists f = Exists (forall a. f (a :: #Type))
We can follow and reimplement (<Ctrl-c><Ctrl-v> ;-)) definitions from Data.Exists and build a set of tools to work with such Exists values:
module Main where
import Prelude
import Unsafe.Coerce (unsafeCoerce)
import Data.Newtype (class Newtype, unwrap)
newtype Exists f = Exists (forall a. f (a :: #Type))
mkExists :: forall f a. f a -> Exists f
mkExists r = Exists (unsafeCoerce r :: forall a. f a)
runExists :: forall b f. (forall a. f a -> b) -> Exists f -> b
runExists g (Exists f) = g f
Using them we get the ability to build an Array of Records with "any" tail but we have to wrap any such a record type in a newtype before:
newtype R t = R { x :: Int | t }
derive instance newtypeRec :: Newtype (R t) _
Now we can build an Array using mkExists:
arr :: Array (Exists R)
arr = [ mkExists (R { x: 8, y : "test"}), mkExists (R { x: 9, z: 10}) ]
and process values using runExists:
x :: Array [ Int ]
x = map (runExists (unwrap >>> _.x)) arr

Related

API for handling polymothinc records

It is a little bit custom issue, is not contrived, but just simplified as possible.
-- this record that has fn that handles both x and y,
-- x and y supposed to be Functors, a arbitrary param for x/y, r is arbitrary result param
type R0 a x y r =
{ fn :: x a -> y a -> r
}
-- this record that has fn that handles only x
type R1 a x r =
{ fn :: x a -> r
}
What I want is a common API (function) that could handle values of R0 and R1 types.
So I do a sum type
data T a x y r
= T0 (R0 a x y r)
| T1 (R1 a x r)
And I declare this function, there is a constraint that x and y have to be Functors.
some :: ∀ a x y r.
Functor x =>
Functor y =>
T a x y r -> a
some = unsafeCoerce -- just stub
Then try to use it.
data X a = X { x :: a}
data Y a = Y { y :: a }
-- make X type functor
instance functorX :: Functor X where
map fn (X val) = X { x: fn val.x }
-- make Y type functor
instance functorY :: Functor Y where
map fn (Y val) = Y { y: fn val.y }
-- declare functions
fn0 :: ∀ a. X a -> Y a -> Unit
fn0 = unsafeCoerce
fn1 :: ∀ a. X a -> Unit
fn1 = unsafeCoerce
Trying to apply some:
someRes0 = some $ T0 { fn: fn0 } -- works
someRes1 = some $ T1 { fn: fn1 } -- error becase it can not infer Y which should be functor but is not present in f1.
So the question is: Is it possible to make such API work somehow in a sensible/ergonomic way (that would not require some addition type annotations from a user of this API)?
I could apparently implement different functions some0 and some1 for handling both cases, but I wonder if the way with a single function (which makes API surface simpler) is possilbe.
And what would be other suggestions for implementing such requirements(good API handling such polymorphic record types that differ in a way described above, when one of the records has exessive params)?
You should make T1 and T0 separate types and then make function some itself overloaded to work with them both:
data T0 x y r a = T0 (R0 a x y r)
data T1 x r a = T1 (R1 a x r)
class Some t where
some :: forall a. t a -> a
instance someT0 :: (Functor x, Functor y) => Some (T0 x y r) where
some = unsafeCoerce
instance someT1 :: Functor x => Some (T1 x r) where
some = unsafeCoerce
An alternative, though much less elegant, solution would be to have the caller of some explicitly specify the y type with a type signature. This is the default approach in situations when a type can't be inferred by the compiler:
someRes1 :: forall a. a
someRes1 = some (T1 { fn: fn1 } :: T a X Y Unit)
Note that I had to add a type signature for someRes1 in order to have the type variable a in scope. Otherwise I couldn't use it in the type signature T a X Y Unit.
An even more alternative way to specify y would be to introduce a dummy parameter of type FProxy:
some :: ∀ a x y r.
Functor x =>
Functor y =>
FProxy y -> T a x y r -> a
some _ = unsafeCoerce
someRes0 = some FProxy $ T0 { fn: fn0 }
someRes1 = some (FProxy :: FProxy Maybe) $ T1 { fn: fn1 }
This way you don't have to spell out all parameters of T.
I provided the latter two solutions just for context, but I believe the first one is what you're looking for, based on your description of the problem mentioning "polymorphic methods". This is what type classes are for: they introduce ad-hoc polymorphism.
And speaking of "methods": based on this word, I'm guessing those fn functions are coming from some JavaScript library, right? If that's the case, I believe you're doing it wrong. It's bad practice to leak PureScript-land types into JS code. First of all JS code might accidentally corrupt them (e.g. by mutating), and second, PureScript compiler might change internal representations of those types from version to version, which will break your bindings.
A better way is to always specify FFI bindings in terms of primitives (or in terms of types specifically intended for FFI interactions, such as the FnX family), and then have a layer of PureScript functions that transform PureScript-typed parameters to those primitives and pass them to the FFI functions.

generalize purescript function using MonadAff

I have a question about generalizing. Starting with this function:
test0 :: String -> String
test0 s = s
we can generalize it in its argument:
test1 :: forall a. Show a => a -> String
test1 s = show s
or in its functional result:
test12 :: forall a. Show a => String -> a
test12 s = s
Now consider the following function:
test2 :: forall e. Aff e Int
test2 s = pure 0
I would like to generalize it in its functional result:
test3 :: forall e m. MonadAff e m => m e Int
test3 s = pure 0
However, I now get an error:
Could not match kind type with kind # Control.Monad.Eff.Effect while checking the kind of MonadAff e m => m e Int in value declaration test3.
I cannot understand why. Moreover, I've found an example of similar such generalizing in Hyper.Node.Server, for example in this type:
write :: forall m e. MonadAff e m => Buffer -> NodeResponse m e
The constraint MonadAff e m asserts that the monad m somehow wraps Aff e somewhere inside. But it doesn't assert that monad m itself must have a type argument e. That would be awfully restrictive, wouldn't it?
Therefore, when constructing your return type, don't apply m to e:
test3 :: forall e m. MonadAff e m => m Int
test3 = pure 0
The example you found is quite different. Here, the function is not returning a value in m, like in your test3, but rather a value NodeResponse, which is a wrapper around a function that returns m Unit.

Higher kinded Types and Type arguments for classes

Suppose you are using the FlexibleInstances extension and have the class
class C a where
f :: a b -> Maybe b
how would you implement it for a list of lists of a datatype. In particular, how would the type be written. The only thing I could find is how to do it for a single list, but not a list of lists or lists of any other datatypes.
This works:
instance C [] where
...
But this doesn't
data D = ...
instance C [[D]] where
...
How can I express something like this?
You need a newtype
class C a where
f :: a b -> b -- the class before the OP edited
newtype LL a = LL [[a]]
instance C LL where
f (LL xss) = ...
However, it is impossible to write a completely meaningful instance, since if the lists-of-lists is empty, it is impossible to extract an element. the best we could do is
instance C LL where
f (LL xss) = case concat xss of
(x:_) -> x
_ -> error "f: no elements"
I'm not sure if that is a good idea.
As an alternative, you could use type families or functional dependencies. Here's a solution with type families.
{-# LANGUAGE TypeFamilies, FlexibleInstances #-}
class C a where
type T a
f :: a -> Maybe (T a)
instance C [[b]] where
type T [[b]] = b
f xss = case concat xss of
[] -> Nothing
(x:_) -> Just x

What does >>= mean in purescript?

I was reading the purescript wiki and found following section which explains do in terms of >>=.
What does >>= mean?
Do notation
The do keyword introduces simple syntactic sugar for monadic
expressions.
Here is an example, using the monad for the Maybe type:
maybeSum :: Maybe Number -> Maybe Number -> Maybe Number
maybeSum a b = do
n <- a
m <- b
let result = n + m
return result
maybeSum takes two
values of type Maybe Number and returns their sum if neither number is
Nothing.
When using do notation, there must be a corresponding
instance of the Monad type class for the return type. Statements can
have the following form:
a <- x which desugars to x >>= \a -> ...
x which desugars to x >>= \_ -> ... or just x if this is the last statement.
A let binding let a = x. Note the lack of the in keyword.
The example maybeSum desugars to ::
maybeSum a b =
a >>= \n ->
b >>= \m ->
let result = n + m
in return result
>>= is a function, nothing more. It resides in the Prelude module and has type (>>=) :: forall m a b. (Bind m) => m a -> (a -> m b) -> m b, being an alias for the bind function of the Bind type class. You can find the definitions of the Prelude module in this link, found in the Pursuit package index.
This is closely related to the Monad type class in Haskell, which is a bit easier to find resources. There's a famous question on SO about this concept, which is a good starting point if you're looking to improve your knowledge on the bind function (if you're starting on functional programming now, you can skip it for a while).

Implementing List#flatten in Haskell

Scala offers a List#flatten method for going from List[Option[A]] to List[A].
scala> val list = List(Some(10), None)
list: List[Option[Int]] = List(Some(10), None)
scala> list.flatten
res11: List[Int] = List(10)
I attempted to implement it in Haskell:
flatten :: [Maybe a] -> [a]
flatten xs = map g $ xs >>= f
f :: Maybe a -> [Maybe a]
f x = case x of Just _ -> [x]
Nothing -> []
-- partial function!
g :: Maybe a -> a
g (Just x) = x
However I don't like the fact that g is a partial, i.e. non-total, function.
Is there a total way to write such flatten function?
Your flatten is the same as catMaybes (link) which is defined like this:
catMaybes :: [Maybe a] -> [a]
catMaybes ls = [x | Just x <- ls]
The special syntax Just x <- ls in a list comprehension means to draw an element from ls and discard it if it is not a Just. Otherwise assign x by pattern matching the value against Just x.
A slight modification of the code you have will do the trick:
flatten :: [Maybe a] -> [a]
flatten xs = xs >>= f
f :: Maybe a -> [a]
f x = case x of Just j -> [j]
Nothing -> []
If we extract the value inside of the Just constructor in f, we avoid g altogether.
Incidentally, f already exists as maybeToList and flatten is called catMaybes, both in Data.Maybe.
One could quite easily write a simple recursive function which goes through a list and rejects all the Nothings from the Maybe monad. Here's how I'd do it as a recursive sequence:
flatten :: [Maybe a] -> [a]
flatten [] = []
flatten (Nothing : xs) = flatten xs
flatten (Just x : xs) = x : flatten xs
However, it may be clearer to write it as a fold:
flatten :: [Maybe a] -> [a]
flatten = foldr go []
where go Nothing xs = xs
go (Just x) xs = x : xs
Or, we could use a blindingly elegant solution thanks to #user2407038, which I'd recommend playing around with in GHCi to work out the individual functions' jobs:
flatten :: [Maybe a] -> [a]
flatten = (=<<) (maybe [] (:[])
And it's faster, folded brother:
flatten :: [Maybe a] -> [a]
flatten = foldr (maybe id (:))
Your solution is halfway there. My suggestion if to rewrite your function f to use pattern matching (like my temporary go function), and enclose it in a where statement to keep relevant functions in one place. You've got to remember the differences in function syntax within scala and Haskell.
The big problem you're having is you don't know the differences I've mentioned. Your g function can use pattern matching with multiple patterns:
g :: Maybe a -> [a]
g (Just x) = [x]
g Nothing = []
There you go: your g function is now what you call 'complete', though more accurately, it would be said to have exhaustive patterns.
You can find more about function syntax here.