Exposing PureScript types to JavaScript - haskell

Can I use PureScript types in JS? For example:
type SomeType = /*type declaration*/
func :: SomeType
func = /*type construction*/
And then in JS do something like this:
let a = module.func()
a instanceof module.SomeType === true
If not if there is any Haskellish language where I can achieve it (Elm, GHCJS, Idris?)?

In general the answer is "no". The runtime representation really depends on what the type is exactly.
For data declarations, every constructor would be represented by a JS "class", like this:
-- PureScript:
data T = A Int | B
x :: T
x = A 42
// JS:
function A(value0) { this.value0 = value0; }
A.create = function(value0) { return new A(value0); }
function B() { }
B.value = new B();
var x = A.create(42);
So you can technically use instanceof to distinguish between cases, but not to determine the type itself.
For a newtype, on the other hand, the type wrapper will be completely erased, because that's the whole point of newtype:
-- PureScript:
newtype T = T Int
x :: T
x = T 42
// JS:
var x = 42;
And for type synonyms (declared with the type keyword), there is no distinct representation. That is, the way such types are represented at runtime is the same as their right-hand side representation:
-- PureScript
type T = Int
x :: T
x = 42
// JS:
var x = 42;
This also applies to naked records, because they are merely type synonyms too:
-- PureScript
type R = { x :: Int, y :: String }
r :: R
r = { x: 42, y: "foo" }
// JS:
var r = { x: 42, y: "foo" }
So the bottom line is: there is no good way to do this. PureScript simply makes no promises about runtime representation. It makes very good promises about program behavior, but not about how it would be implemented in JS (or whatever other backend you happen to be using).
And this is another problem: because PureScript doesn't make any promises like that, it's a very bad idea to rely on your (mine) knowledge of the current implementation, because the next version of the compiler may change it completely. PureScript can do this, because it made no promises. See how that works?
For example, I know there is a pull request in the pipeline somewhere that would switch data representation to arrays, like this:
-- PureScript:
data T = A Int | B
x, y :: T
x = A 42
y = B
// JS:
var x = ["A", 42]
var y = ["B"]
If this pull request is ever accepted, your program will be broken.
In conclusion I can also tell you that neither Elm, nor Haskell, nor Idris will exhibit the property you're after. Runtime type information is generally not a thing valued in the ML universe. One can argue it's effectively present in Idris, in a way, but not in the way you expect.
You may want to try F#/Fable: it's a little bit stronger in this regard, but still not as good as on its native .NET runtime. I don't think you can just check types of F# objects from JS out of the blue, but I know that Fable does support accessing RTTI from F# itself (at extra performance cost), so you can at least make functions to do that and export them to JS.
If you can expand a bit on what your actual problem is, then perhaps I (or someone else) would be able to suggest an alternative solution.

Related

What is the way to implement Observable other than Control.Lens.Setter in Haskell?

Control.Lens.Setter is remarkable to use Observable feature in Haskell. ( function/functor to be triggered when the value in the dataset is updated. )
However, considering the lens is not included in the standard environment, and then required to extra installation, without the lens, when I want to use just a primitive setter feature such as with a field label:
data Foo = Foo {val :: Int}
How to do this?
Does ST Monad fit this purpose?
Thanks.
It's not very clear what you're looking for here, but if you just want to update a record field, you can use the Haskell record update syntax:
x = Foo { val = 5 }
y = x { val = 42 }
This works for any record, with any number of fields, and you don't need to list all the fields, only the ones you'd like to update, for example:
data D = D { a :: String, b :: Int }
x = D { a = "foo", b = 42 }
y = x { a = "bar" } -- now y = D { a = "bar", b = 42 }
z = x { b = 43 } -- now z = D { a = "foo", b = 43 }
Keep in mind that this doesn't actually update ("change", "mutate") the values in memory, but rather creates copies of the records with all the fields equal to those of the original record, except the updated fields. Lenses work the same way, and in fact everything in Haskell does, since Haskell doesn't allow mutation at all.
I think that you're a bit confused between a few concepts; specifically, setter vs observable.
I don't know much about them, but an observable does seem to be, as you described, a way to run a function every time a value is changed. This concept does not exist in Haskell; I'm sure you could make it somehow, but it wouldn't be very useful, since (almost) every Haskell value is immutable.
By contrast, a setter is just a function which changes part of a value, the same as with Java getters and setters. The only slight complication is that in Haskell, all values are immutable (as I mentioned above), so instead of changing the value, a setter copies the value across but with the thing set. For instance, if you have a setter setName (for a record), and you invoke it with something like setName "foo" oldRecord, this will keep oldRecord as is, but return a new record with the name set to "foo". As you already know, there are other, more complex, implementations of setters, such as lenses.
You also mention ST. This is a more advanced concept; it's basically used when you have to use some sort of mutable variable locally but still retain purity (which is not very often).
Now to answer your other question: How do I use setters without lenses? Well, if you have a record like data Foo = Foo {val :: Int, val2 :: String} (your example + an extra field), and you have an old record - let's say oldRecord = Foo { val = 1, val2 = "test" }, Haskell has special setter syntax: doing oldRecord { val = 2 } will give a new record with val set to 2 - that is, it will give Foo { val = 2, val2 = "test" }. Obviously this syntax is a bit clumsy though for nested records, which is the reason lenses were invented.

Existential type classes vs. Data Constructors vs. Coproducts

I find myself running up against the same pattern in my designs where I start with a type with a few data constructors, eventually want to be able to type against those data constructors and thus split them into their own types, just to then have to increase the verbosity of other parts of the program by needing to use Either or another tagged-union for situations where I still need to represent multiple of these types (namely collections).
I am hoping someone can point me to a better way of accomplishing what I'm trying to do. Let me start with a simple example. I am modeling a testing system, where you can have nested test suites which eventually end in tests. So, something like this:
data Node =
Test { source::string }
Suite { title::string, children::[Node] }
So, pretty simple so far, essentially a fancy Tree/Leaf declaration. However, I quickly realize that I want to be able to make functions that take Tests specifically. As such, I'll now split it up as so:
data Test = Test { source::string }
data Suite = Suite { title::string, children::[Either Test Suite] }
Alternatively I might roll a "custom" Either (especially if the example is more complicated and has more than 2 options), say something like:
data Node =
fromTest Test
fromSuite Suite
So, already its pretty unfortunate that just to be able to have a Suite that can have a combination of Suites or Tests I end up with a weird overhead Either class (whether it be with an actual Either or a custom one). If I use existential type classes, I could get away with making both Test and Suite derive "Node_" and then have Suite own a List of said Nodes. Coproducts would allow something similar, where I'd essentially do the same Either strategy without the verbosity of the tags.
Allow me to expand now with a more complex example. The results of the tests can be either Skipped (the test was disabled), Success, Failure, or Omitted (the test or suite could not be run due to a previous failure). Again, I originally started with something like this:
data Result = Success | Omitted | Failure | Skipped
data ResultTree =
Tree { children::[ResultTree], result::Result } |
Leaf Result
But I quickly realized I wanted to be able to write functions that took specific results, and more importantly, have the type itself enforce the ownership properties: A successful suite must only own Success or Skipped children, Failure's children can be anything, Omitted can only own Omitted, etc. So now I end up with something like this:
data Success = Success { children::[Either Success Skipped] }
data Failure = Failure { children::[AnyResult] }
data Omitted = Omitted { children::[Omitted] }
data Skipped = Skipped { children::[Skipped] }
data AnyResult =
fromSuccess Success |
fromFailure Failure |
fromOmitted Omitted |
fromSkipped Skipped
Again, I now have these weird "Wrapper" types like AnyResult, but, I get type enforcement of something that used to only be enforced from runtime operation. Is there a better strategy to this that doesn't involve turning on features like existential type classes?
The first thing that came to my mind reading your sentence: "I quickly realized I wanted to be able to write functions that took specific results" is Refinement Types.
They allow to take only some values from a type as input, and make those constraints compile-time check/error.
There is this video from a talk at HaskellX 2018, that introduces LiquidHaskell, which allows the use of Refinement Types in Haskell:
https://skillsmatter.com/skillscasts/11068-keynote-looking-forward-to-niki-vazou-s-keynote-at-haskellx-2018
You have to decorate your haskell function signature, and have LiquidHaskell installed:
f :: Int -> i : Int {i | i < 3} -> Int would be a function which could only accept as second parameter an Int with a value < 3, checked at compile time.
You might as well put constraints on your Result type.
I think what you may be looking for is GADTs with DataKinds. This lets you refine the types of each constructor in a data type to a particular set of possible values. For example:
data TestType = Test | Suite
data Node (t :: TestType) where
TestNode :: { source :: String } -> Node 'Test
SuiteNode :: { title :: String, children :: [SomeNode] } -> Node 'Suite
data SomeNode where
SomeNode :: Node t -> SomeNode
Then when a function operates only on tests, it can take a Node 'Test; on suites, a Node 'Suite; and on either, a polymorphic Node a. When pattern-matching on a Node a, each case branch gets access to an equality constraint:
useNode :: Node a -> Foo
useNode node = case node of
TestNode source -> {- here it’s known that (a ~ 'Test) -}
SuiteNode title children -> {- here, (a ~ 'Suite) -}
Indeed if you took a concrete Node 'Test, the SuiteNode branch would be disallowed by the compiler, since it can’t ever match.
SomeNode is an existential that wraps a Node of an unknown type; you can add extra class constraints to this if you want.
You can do a similar thing with Result:
data ResultType = Success | Omitted | Failure | Skipped
data Result (t :: ResultType) where
SuccessResult
:: [Either (Result 'Success) (Result 'Skipped)]
-> Result 'Success
FailureResult
:: [SomeResult]
-> Result 'Failure
OmittedResult
:: [Result 'Omitted]
-> Result 'Omitted
SkippedResult
:: [Result 'Skipped]
-> Result 'Skipped
data SomeResult where
SomeResult :: Result t -> SomeResult
Of course I assume in your actual code there’s more information in these types; as it is, they don’t represent much. When you have a dynamic computation such as running a test that may produce different kinds of result, you can return it wrapped in SomeResult.
In order to work with dynamic results, you may need to prove to the compiler that two types are equal; for that, I direct you to Data.Type.Equality, which provides a type a :~: b which is inhabited by a single constructor Refl when the two types a and b are equal; you can pattern-match on this to inform the typechecker about type equalities, or use the various combinators to carry out more complicated proofs.
Also useful in conjunction with GADTs (and ExistentialTypes, less generally) is RankNTypes, which basically enables you to pass polymorphic functions as arguments to other functions; this is necessary if you want to consume an existential generically:
consumeResult :: SomeResult -> (forall t. Result t -> r) -> r
consumeResult (SomeResult res) k = k res
This is an example of continuation-passing style (CPS), where k is the continuation.
As a final note, these extensions are widely used and largely uncontroversial; you needn’t be wary of opting in to (most) type system extensions when they let you express what you mean more directly.

What is the purpose of including the type in its definition in haskell?

I'm a beginner in haskell and I wonder about the right way to define a new type. Suppose I want to define a Point type. In an imperative language, it's usually the equivalent of:
data Point = Int Int
However in haskell I usually see definitions such as:
data Point = Point Int Int
What are the differences and when should each approach be used?
In OO languages you can define a class with something like this
class Point {
int x,y;
Point(int x, int y) {...
}
it's similar
data Point = ...
is the type definition (similar to class Point above , and
... = Point Int Int
is the constructor, you can also define the constructor with a different name, but you need a name regardless.
data Point = P Int Int
The data definitions are, ultimately, tagged unions. For example:
data Maybe a = Nothing | Just a
Now how would you write this type using your syntax?
Moreover it remains the fact that in Haskell you can pattern match over this values and see which constructor was used to build a value. The name of the constructor is needed for pattern matching, and if the type has just one constructor it often re-uses the same name as the type.
For example:
let x = someOperationReturningMaybe
in case x of
Nothing -> 0
Just y -> y+5
This is different from plain union type, such as C's union where you can say "this thing is etiher an int or a float" but you have no way to know which one it actually is (except by keeping track of the state by hand).
Writing the code above using a C union you have no way to use a case to perform different actions depending on the constructor used, and you have to keep track explicitly what type is contained in that x and use an if.

Does Data.Map work in a pass-by-value or pass-by-reference way? (Better explanation inside)

I have a recursive function working within the scope of strictly defined interface, so I can't change the function signatures.
The code compiles fine, and even runs fines without error. My problem is that it's a large result set, so it's very hard to test if there is a semantic error.
My primary question is: In a sequence of function calls A to B to A to B to breaking condition, considering the same original Map is passed to all functions until breaking condition, and that some functions only return an Integer, would an insert on a Map in a function that only returns an Integer still be reflected once control is returned to the first function?
primaryFunc :: SuperType -> MyMap -> (Integer, MyMap)
primaryFunc (SubType1 a) mapInstance = do
let returnInt = func1 a mapInstance
(returnInt, mapInstance)
primaryFunc (SubType2 c) mapInstance = do
let returnInt = primaryFunc_nonprefix_SuperType c mapInstance
let returnSuperType = (Const returnInt)
let returnTable = H.insert c returnSuperType mapInstance
(returnInt, returnTable)
primaryFunc (ConstSubType d) mapInstance = do
let returnInt = d
(returnInt, mapInstance)
func1 :: SubType1 -> MyMap -> Integer
func1 oe vt = do
--do stuff with input and map data to get return int
returnInt = primaryFunc
returnInt
func2 :: SubType2 -> MyMap -> Integer
func2 pe vt = do
--do stuff with input and map data to get return int
returnInt = primaryFunc
returnInt
Your question is almost impossibly dense and ambiguous, but it should be possible to answer what you term your "primary" question from the simplest first principles of Haskell:
No Haskell function updates a value (e.g. a map). At most it can return a modified copy of its input.
Outside of the IO monad, no function can have side effects. No function can affect the value of any variable assigned before it was called; all it can do is return a value.
So if you pass a map as a parameter to a function, nothing the function does can alter your existing reference to that value. If you want an updated value, you can only get that from the output of a function to which you have passed the original value as input. New value, new reference.
Because of this, you should have absolute clarity at any depth within your web of functions about which value you are working with. Knowing this, you should be able to answer your own question. Frankly, this is such a fundamental characteristic of Haskell that I am perplexed that you even need to ask.
If a function only returns an integer, then any operations you perform on any values made available to the function can only affect the output - that is, the integer value returned. Nothing done within the function can affect anything else (short of causing the whole program to crash).
So if function A has a reference to a map and it passes this value to function B which returns an int, nothing function B does can affect A's copy of the map. If function B were allowed to secretly alter A's copy of the map, that would be a side effect. Side effects are not allowed.
You need to understand that Haskell does not have variables as you understand them. It has immutable values, references to immutable values and functions (which take inputs and return new outputs). Functions do not have variables which are in scope for other functions which might alter those variables on the fly. That cannot happen.
As an aside, not only does the code you posted show that you do not understand the basics of Haskell syntax, the question you asked shows that you haven't understood the primary characteristics of Haskell as a language. Not only are these fundamentals things which can be understood before having learned any syntax, they are things you need to know to make sense of the syntax.
If you have a deadline, meet it using a tool you do understand. Then go learn Haskell properly.
In addition, you will find that
an insert on a Map in a function that only returns an Integer
is nearly impossible to express. Yes, you can technically do it like in
insert k v map `seq` 42 -- force an insert and throw away the result
but if you think that, for example:
let done = insert k v map in 42
does anything with the map, you're probably wrong.
In no case, however, is the original map altered.

Idiomatic way to modify a member variable

I know Haskell isn't OO so it isn't strictly a 'member variable'.
data Foo = Foo {
bar :: Int,
moo :: Int,
meh :: Int,
yup :: Int
}
modifyBar (Foo b m me y) = (Foo b' m me y)
where b' = 2
This is how my code looks at the moment. The problem is I am now making data types with 16 or more members. When I need to modify a single member it results in very verbose code. Is there a way around this?
modifyBar foo = foo { bar = 2 }
This syntax will copy foo, and then modify the bar field of that copy to 2. This could be naturally extended to more fields, so you don't need to write that modifyBar function at all.
(See http://book.realworldhaskell.org/read/code-case-study-parsing-a-binary-data-format.html#id625467)
Haskell's "record syntax" that #KennyTM shows is the built-in way to do this, though keep in mind that it's still just a way of constructing a new value based on the old one.
There are some annoying limitations to record syntax, though, particularly that the form used to "modify" a single item in a record aren't first-class entities in the language, so you can't abstract over them and pass them around the way you'd do with a regular function.
An alternative is using a library such as fclabels which provides similar functionality, using Template Haskell to auto-generate accessor functions instead of built-in syntax. The result is often much nicer, with the downside that you now have a dependency on TH....

Resources