Case split on a maybe type on PureScript - purescript

I am learning PureScript by following PureScript by example, and by chapter 3 (rest of definitions there) there is an exercise to delete duplicate entries from a bog, but I cannot get to work the case split for the Maybe (Nothing/Just e). I checked also the syntax guide but I cannot catch the pitfall and keep getting Unknown data constructor Nothing.
import Prelude
import Data.AddressBook
import Control.Plus (empty)
import Data.List (List(..), (:), filter, head, foldl, tail)
import Data.Maybe (Maybe, isJust)
import Data.Newtype (overF)
skipIfDup :: AddressBook -> AddressBook -> AddressBook
skipIfDup newBook (Nil : book) = newBook
skipIfDup newBook (entry : book) =
skipIfDup newerBook book
where
newerBook :: AddressBook
newerBook =
case findEntry entry.firstName entry.lastName newerBook of
Nothing -> newBook
Just e -> insertEntry e newBook

"Unknown data constructor Nothing" means just what it says: the compiler doesn't know what Nothing is, where it's defined.
And how do you let it know where it's defined? Same way as with all the other stuff you're using in your program - with an import!
You already have import Data.Maybe (Maybe), but that's not enough: this is importing only the type Maybe, but not its constructors.
To import the Nothing constructor, add it after the type in parens like this:
import Data.Maybe (Maybe(Nothing), isJust)
Or you can import all constructors that exist by using two dots instead:
import Data.Maybe (Maybe(..), isJust)
The latter is the more accepted pattern.

Related

clearing out all child elements from a div with PureScript

There are lots of ways to empty out a element of a DOM node with JavaScript. I would like to do this with PureScript (with the intent of replacing static content with a Halogen widget). It seems like among all the functions of purescript-web-html and purescript-web-dom there ought to be an obvious way to to this, but the huge number of conflicting ways to describe a element are defeating me.
Is there a one or two line easy way to do what seems like an obvious operation before calling runUI?
I'm not sure if this is the best way to do this as it seems like there ought to be a nice functional way to map over an HTMLCollection, but at least it works:
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Halogen.Aff as HA
import Halogen.VDom.Driver (runUI)
import Web.DOM.ParentNode
import Web.DOM.ChildNode (remove)
import Web.HTML.HTMLElement (HTMLElement, toParentNode)
import Web.DOM.Element (toChildNode)
import Effect.Class
selector :: QuerySelector
selector = QuerySelector "#halogen"
main :: Effect Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
elem <- HA.selectElement selector
case elem of
Nothing -> runUI component unit body
Just e -> do
liftEffect $ clearChildren e
runUI component unit e
clearChildren :: HTMLElement -> Effect Unit
clearChildren e = clearNextChild (toParentNode e)
where
clearNextChild :: ParentNode -> Effect Unit
clearNextChild n = do
last <- lastElementChild n
case (last) of
Nothing -> pure unit
Just elem -> do
remove $ toChildNode elem
clearNextChild n

Purescript Import infix type constrctor

Here, \/ is from Data.Either.
This example is copied from the link above:
f :: (Int \/ String \/ Boolean) -> String
f = show \/ identity \/ if _ then "Yes" else "No"
What does the import statement look like?
Here you need to import both the type \/ and the value \/.
The syntax for importing type operators is type (\/). The prefix type is necessary for disambiguation - that is, to let the compiler know that you're importing the type, not the value that might have the same name.
And the syntax for importing the value is as usual.
So the whole import would look like this:
import Data.Either.Nested (type (\/), (\/))
In conclusion, I would recommend using your IDE integration (for example, here's a VSCode extension) to insert imports for you. That way you don't have to know the precise syntax.

Are there any triple equals === methods outside of Cats in Scala?

I have spent a while searching on Google for a non-Cats triple equals method, but can't find anything apart from Scalaz. Unfortunately, I have been unable to work out the import for === in this library.
Can someone help, many thanks.
If all you need is ===, you could very easily mimic the behaviour from Cats with your own function:
implicit class AnyWithTripleEquals[T](a: T) {
def ===(b: T): Boolean = a equals b
}
/*
scala> "2" === "3"
res0: Boolean = false
scala> "2" === 3
<console>:13: error: type mismatch;
found : Int(3)
required: String
"2" === 3
*/
Regarding scalaz try
import scalaz._
import Scalaz._
42 === "hello" // error: type mismatch; found: String("hello") required: Int
where
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.2.28"
Of the top of my head other libraries that use === are e.g.:
Slick uses === for = in queries - http://slick.lightbend.com/doc/3.3.1/queries.html
matchers in test frameworks e.g. https://github.com/etorreborre/specs2/blob/8305db76c5084e4b3ce5827ce23117f6fb6beee4/matcher/shared/src/main/scala/org/specs2/matcher/TypedEqual.scala use === to check assertions and (usually) raise assertion error, sometimes with some nice diff (scalatest I think?)
but it isn't the same use case as in Cats/Scalaz.
If you want to use it in Cats you need:
syntax - import cats.syntax.eq._ or import cats.syntax.all._ or import cats.implicits._ (if you duplicate import of syntax, Scala won't be able to resove it)
instance - if you compare 2 A you need an implicit instance of cats.Eq[A]. instances for Lists, Maps etc. can be found in cats.instances.list._, cats.instances.map._, cats.instances.all._ or cats.implicits._ (same rule as above). There should be instances for all "normal" types but if you have your own, you need to either provide Eq instance on your own or derive it with something like Kittens.
If you are missing some implicit (or if some implicit is ambiguous, because you imported the same things from 2 different places) the syntax won't work.
Same thing about Scalaz though imports and type classes will have other names.
If you don't mind some performance penalty (caused by isInstanceOf inside equals) and lack of flexibility regarding the definition of equality check, you can use #sachav's solution.
Another library that provides === is scalactic, which is basically a set of utilities used by ScalaTest, which are packaged as a separate lib.
import org.scalactic._
import TypeCheckedTripleEquals._
"Hello" === "Hello" //true
1 === "Hello" //won't compile
you can also "configure" how your equality is being resolved with implicits:
import org.scalactic._
import TripleEquals._
import StringNormalizations._
import Explicitly._
implicit val strEquality = decided by defaultEquality[String] afterBeing lowerCased
"Hello" === "hello" // true
"normalized" === "NORMALIZED" // true

Purescript cannot get keycode from keyboard

I am new to functional programming and to Purescript. I am trying to get keycode from keys pressed from keyboard. I have made a eventListener which fires when keydown event is fired and triggers event listener function test. I am having problem in converting event to keyboardevent and getting keycode from keyboardevent. I am attaching my code and error it producing.
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Class
import Control.Monad.Eff.Console (CONSOLE, log)
import DOM (DOM)
import DOM.Event.EventTarget (addEventListener, eventListener)
import DOM.HTML.Types as DHT
import DOM.Event.KeyboardEvent as KE
import DOM.Event.Types (EventType(..), EventTarget)
import DOM.Event.Event
import DOM.HTML (window)
import DOM.HTML.Window (document)
import Prelude (Unit)
test :: forall e. Event -> Eff ( console :: CONSOLE, dom :: DOM | e) Unit
test a = do
ke <- KE.eventToKeyboardEvent a
co <- KE.key ke
log "Key Pressed : "
main :: forall e. Eff (console :: CONSOLE, dom :: DOM | e) Unit
main = do
documenttarget <- liftEff $ window >>= document <#> DHT.htmlDocumentToEventTarget
addEventListener (EventType "keydown") (eventListener test) true (documenttarget)
Error :
Error found:
in module Main
at src/Main.purs line 30, column 10 - line 30, column 19
Could not match type
String
with type
t0 t1
while checking that type String
is at least as general as type t0 t1
while checking that expression key ke
has type t0 t1
in value declaration test
where t0 is an unknown type
t1 is an unknown type
See https://github.com/purescript/documentation/blob/master/errors/TypesDoNotUnify.md for more information,
or to contribute content related to this error.
* ERROR: Subcommand terminated with exit code 1
Your test function needs tweaking a bit:
import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Class (liftEff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Control.Monad.Except (runExcept)
import DOM (DOM)
import DOM.Event.Event (Event)
import DOM.Event.EventTarget (addEventListener, eventListener)
import DOM.Event.KeyboardEvent as KE
import DOM.Event.Types (EventType(..))
import DOM.HTML (window)
import DOM.HTML.Types as DHT
import DOM.HTML.Window (document)
import Data.Either (Either(..))
test :: forall e. Event -> Eff ( console :: CONSOLE, dom :: DOM | e) Unit
test a =
case runExcept (KE.eventToKeyboardEvent a) of
Left err ->
log "Event was not a keyboard event"
Right ke -> do
let co = KE.key ke
log "Key Pressed : "
main :: forall e. Eff (console :: CONSOLE, dom :: DOM | e) Unit
main = do
documenttarget <- liftEff $ window >>= document <#> DHT.htmlDocumentToEventTarget
addEventListener (EventType "keydown") (eventListener test) true (documenttarget)
First, KE.key and KE.eventToKeyboardEvent are not effectful functions, which means you don't need to bind to get the values from them (this is what the t0 t1 error was about: it expects the resulting value to be like m String rather than just String, for some m).
Next, when we use eventToKeyboardEvent, it can fail, since we have no way of guaranteeing that it's safe to coerce any Event to a KeyboardEvent. The result is wrapped in F, which is a synonym for Except MultipleErrors, so we need to do something to get the result of that. We "run" the Except wrapper that gives us an Either back, where the Left side is the error message and Right is the success result.
In this example I made it log when the cast fails, but you probably don't care about that, and generally don't even expect that case to be hit, in which case using pure unit is probably good enough instead.
There's actually another way of writing this if you're happy to let the failure case do nothing:
import Data.Foldable (for_)
test :: forall e. Event -> Eff ( console :: CONSOLE, dom :: DOM | e) Unit
test a =
for_ (runExcept (KE.eventToKeyboardEvent a)) \ke -> do
let co = KE.key ke
log "Key Pressed : "
for_ is actually a highly generic function that can be used for a variety of stuff, but this usage of it to absorb failures silently is pretty handy when writing code against the ugly DOM API.

How do you set the document title using Purescript?

After searching for some time I found in Pursuit the module DOM.HTML.History which has the data type DocumentTitle. This type could probably be used together with the function
replaceState ::
∀ e. Foreign -> DocumentTitle -> URL -> History -> Eff (history :: HISTORY | e) Unit
To change the document.title property of the page, however, I can't find examples showing how to call this function (e.g., where do I get the external Foreign data type?). Also, I'm not even sure if this function would do what I expect it to do...
In the unfortunate case that the Purescript team didn't include in their core API a way to change the document title, it's still possible to do so by making use of purescript's handy FFI mechanism.
Add these two files into your project:
Document.js
exports.setDocumentTitle =
function (title)
{
return function ()
{
window.document.title = title;
};
};
Document.purs
module Document
where
import Control.Monad.Eff (kind Effect, Eff)
import Data.Unit (Unit)
foreign import data DOCUMENT :: Effect
foreign import setDocumentTitle ::
∀ fx . String -> Eff (document :: DOCUMENT | fx) Unit
Now you can call setDocumentTitle as you would call Console's log function, except the effect would be DOCUMENT instead of CONSOLE, of course.
kazouas answer would look like this (in PS 0.12)
import Effect (Effect)
import Data.Unit (Unit)
foreign import setDocumentTitle :: String -> Effect Unit
Javascript remains the same.