I'm trying to convert the following ES6 script to bucklescript and I cannot for the life of me figure out how to create a "closure" in bucklescript
import {Socket, Presence} from "phoenix"
let socket = new Socket("/socket", {
params: {user_id: window.location.search.split("=")[1]}
})
let channel = socket.channel("room:lobby", {})
let presence = new Presence(channel)
function renderOnlineUsers(presence) {
let response = ""
presence.list((id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
response += `<br>${id} (count: ${count})</br>`
})
document.querySelector("main[role=main]").innerHTML = response
}
socket.connect()
presence.onSync(() => renderOnlineUsers(presence))
channel.join()
the part I cant figure out specifically is let response = "" (or var in this case as bucklescript always uses vars):
function renderOnlineUsers(presence) {
let response = ""
presence.list((id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
response += `<br>${id} (count: ${count})</br>`
})
document.querySelector("main[role=main]").innerHTML = response
}
the closest I've gotten so far excludes the result declaration
...
...
let onPresenceSync ev =
let result = "" in
let listFunc = [%raw begin
{|
(id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
result += `${id} (count: ${count})\n`
}
|}
end
] in
let _ =
presence |. listPresence (listFunc) in
[%raw {| console.log(result) |} ]
...
...
compiles to:
function onPresenceSync(ev) {
var listFunc = (
(id, {metas: [first, ...rest]}) => {
let count = rest.length + 1
result += `${id} (count: ${count})\n`
}
);
presence.list(listFunc);
return ( console.log(result) );
}
result is removed as an optimization beacuse it is considered unused. It is generally not a good idea to use raw code that depends on code generated by BuckleScript, as there's quite a few surprises you can encounter in the generated code.
It is also not a great idea to mutate variables considered immutable by the compiler, as it will perform optimizations based on the assumption that the value will never change.
The simplest fix here is to just replace [%raw {| console.log(result) |} ] with Js.log result, but it might be enlightening to see how listFunc could be written in OCaml:
let onPresenceSync ev =
let result = ref "" in
let listFunc = fun [#bs] id item ->
let count = Js.Array.length item##meta in
result := {j|$id (count: $count)\n|j}
in
let _ = presence |. (listPresence listFunc) in
Js.log !result
Note that result is now a ref cell, which is how you specify a mutable variable in OCaml. ref cells are updated using := and the value it contains is retrieved using !. Note also the [#bs] annotation used to specify an uncurried function needed on functions passed to external higher-order functions. And the string interpolation syntax used: {j| ... |j}
Related
I passed a vec parameter into function in rust like this:
pub fn songs(playlist_records: Vec<QueryFavorites>) -> Vec<MusicResponse> {
}
first step I get the ids from playlist_records to execute a query, this is the code:
let ids: Vec<String> = playlist_records
.into_iter()
.map(|item| item.source_id.to_string())
.collect();
when I use the playlist_records second time to fetch the field after get the query result:
for query_song in &results {
let favTime: Vec<QueryFavorites> = playlist_records
.into_iter()
.filter(|item| item.source_id == query_song.source_id)
.collect();
}
it tell me the playlist_records was moved. This is the message:
`playlist_records` moved due to this method call, in previous iteration of loop
I tried to borrow the playlist_records like this:
let ids: Vec<String> = &playlist_records
.into_iter()
.map(|item| item.source_id.to_string())
.collect();
seems did not work, what should I do to reuse the playlist_records?
Change into_iter to use iter, since into_iter consumes the collection while iter iterates by reference and doesn't consume, see more about the difference here:
playground
let ids: Vec<String> = playlist_records
.iter()
.map(|item| item.source_id.to_string())
.collect();
let favTime: Vec<&QueryFavorites> = playlist_records
.iter()
.filter(|item| item.source_id == "someid")
.collect();
which however means your favTime will be of Vec<&QueryFavorites> type if that's fine with you. Otherwise, you can always clone the iterator.
let favTime: Vec<QueryFavorites> = playlist_records
.iter()
.cloned()
.filter(|item| item.source_id == "someid")
.collect();
playground
I need to modify the data to give input to CEP system, my current data looks like below
val rdd = {"var":"system-ready","value":0.0,"objectID":"2018","partnumber":2,"t":"2017-08-25 11:27:39.000"}
I need output like
t = "2017-08-25 11:27:39.000
Check = { var = "system-ready",value = 0.0, objectID = "2018", partnumber = 2 }
I have to write RDD map operations to achieve this if anybody suggests better option welcome. colcount is the number of columns.
rdd.map(x => x.split("\":").mkString("\" ="))
.map((f => (f.dropRight(1).split(",").last.toString, f.drop(1).split(",").toSeq.take(colCount-1).toString)))
.map(f => (f._1, f._2.replace("WrappedArray(", "Check = {")))
.map(f => (f._1.drop(0).replace("\"t\"", "t"), f._2.dropRight(1).replace("(", "{"))) /
.map(f => f.toString().split(",C").mkString("\nC").replace(")", "}").drop(0).replace("(", "")) // replacing , with \n, droping (
.map(f => f.replace("\" =\"", "=\"").replace("\", \"", "\",").replace("\" =", "=").replace(", \"", ",").replace("{\"", "{"))
Scala's JSON parser seems to be a good choice for this problem:
import scala.util.parsing.json
rdd.map( x => {
JSON.parseFull(x).get.asInstanceOf[Map[String,String]]
})
This will result in an RDD[Map[String, String]]. You can then access the t field from the JSON, for example, using:
.map(dict => "t = "+dict("t"))
My version of RegEx is being greedy and now working as it suppose to. I need extract each message with timestamp and user who created it. Also if user has two or more consecutive messages it should go inside one match / block / group. How to solve it?
https://regex101.com/r/zD5bR6/1
val pattern = "((a\.b|c\.d)\n(.+\n)+)+?".r
for(m <- pattern.findAllIn(str).matchData; e <- m.subgroups) println(e)
UPDATE
ndn solution throws StackOverflowError when executed:
Exception in thread "main" java.lang.StackOverflowError
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4708)
.......
Code:
val pattern = "(?:.+(?:\\Z|\\n))+?(?=\\Z|\\w\\.\\w)".r
val array = (pattern findAllIn str).toArray.reverse foreach{println _}
for(m <- pattern.findAllIn(str).matchData; e <- m.subgroups) println(e)
I don't think a regular expression is the right tool for this job. My solution below uses a (tail) recursive function to loop over the lines, keep the current username and create a Message for every timestamp / message pair.
import java.time.LocalTime
case class Message(user: String, timestamp: LocalTime, message: String)
val Timestamp = """\[(\d{2})\:(\d{2})\:(\d{2})\]""".r
def parseMessages(lines: List[String], usernames: Set[String]) = {
#scala.annotation.tailrec
def go(
lines: List[String], currentUser: Option[String], messages: List[Message]
): List[Message] = lines match {
// no more lines -> return parsed messages
case Nil => messages.reverse
// found a user -> keep as currentUser
case user :: tail if usernames.contains(user) =>
go(tail, Some(user), messages)
// timestamp and message on next line -> create a Message
case Timestamp(h, m, s) :: msg :: tail if currentUser.isDefined =>
val time = LocalTime.of(h.toInt, m.toInt, s.toInt)
val newMsg = Message(currentUser.get, time, msg)
go(tail, currentUser, newMsg :: messages)
// invalid line -> ignore
case _ =>
go(lines.tail, currentUser, messages)
}
go(lines, None, Nil)
}
Which we can use as :
val input = """
a.b
[10:12:03]
you can also get commands
[10:11:26]
from the console
[10:11:21]
can you check if has been resolved
[10:10:47]
ah, okay
c.d
[10:10:39]
anyways startsLevel is still 4
a.b
[10:09:25]
might be a dead end
[10:08:56]
that need to be started early as well
"""
val lines = input.split('\n').toList
val users = Set("a.b", "c.d")
parseMessages(lines, users).foreach(println)
// Message(a.b,10:12:03,you can also get commands)
// Message(a.b,10:11:26,from the console)
// Message(a.b,10:11:21,can you check if has been resolved)
// Message(a.b,10:10:47,ah, okay)
// Message(c.d,10:10:39,anyways startsLevel is still 4)
// Message(a.b,10:09:25,might be a dead end)
// Message(a.b,10:08:56,that need to be started early as well)
The idea is to take as little characters as possible that will be followed by a username or the end of the string:
(?:.+(?:\Z|\n))+?(?=\Z|\w\.\w)
See it in action
I'm doing my first dive into F# at work, and I'm moving several C# unit tests I have to F# as an exercise. Our tests are quite complex, but I relish the challenge (With setups, inheritance, teardowns and so on).
As I've been seeing, mutability should be avoided if possible, but when writing the [SetUp] parts of the tests I can't seem to find a way to jump over mutability. Example that creates a dummy XML for a test::
[<TestFixture>]
type CaseRuleFixture() =
[<DefaultValue>] val mutable xsl : XNamespace
[<DefaultValue>] val mutable simpleStylesheet : XElement
[<DefaultValue>] val mutable testNode : XElement
[<DefaultValue>] val mutable rootNode : XElement
[<DefaultValue>] val mutable root : XElement
let CreateXsltHeader(xsl: XNamespace) =
// Build XSLT header
let styleSheetRoot =
new XElement(
xsl + "stylesheet",
new XAttribute(XName.Get "version", "1.0"),
new XAttribute(XNamespace.Xmlns + "xsl", "http://www.w3.org/1999/XSL/Transform"),
new XAttribute(XNamespace.Xmlns + "msxsl", "urn:schemas-microsoft-com:xslt"),
new XAttribute(XName.Get "exclude-result-prefixes", "msxsl"),
new XAttribute(XNamespace.Xmlns + "utils", "urn:myExtension"))
let outputNode =
new XElement(
xsl + "output",
new XAttribute(XName.Get "method", "xml"),
new XAttribute(XName.Get "indent", "yes"))
styleSheetRoot.Add outputNode
styleSheetRoot
[<SetUp>]
member this.SetUp() =
this.xsl <- XNamespace.Get "http://www.w3.org/1999/XSL/Transform"
this.simpleStylesheet <- CreateXsltHeader(this.xsl)
Directory.EnumerateFiles "Templates"
|> Seq.iter(fun filepath -> this.simpleStylesheet.Add(XElement.Parse(File.ReadAllText filepath).Elements()))
let variable =
new XElement(
this.xsl + "variable",
new XAttribute(XName.Get "name", "ROOT"),
new XAttribute(XName.Get "select", "ROOT"))
this.simpleStylesheet.Add(variable)
let rootTemplate = new XElement(this.xsl + "template", new XAttribute(XName.Get "match", "/ROOT"))
this.simpleStylesheet.Add(rootTemplate);
this.rootNode <- new XElement(XName.Get "ROOT")
rootTemplate.Add(this.rootNode);
this.root <- new XElement(XName.Get "ROOT")
this.testNode <- new XElement(XName.Get "TESTVALUE")
this.root.Add(this.testNode)
[<Test>]
member this.CaseCapitalizeEachWordTest() =
this.testNode.Value <- " text to replace ";
let replaceRule = new CaseRule();
replaceRule.Arguments <- [| "INITIALS" |];
this.rootNode.Add(
replaceRule.ApplyRule [| new XElement(this.xsl + "value-of", new XAttribute(XName.Get "select", "TESTVALUE")) |]);
let parser = new XsltParserHelper(this.simpleStylesheet);
let result = parser.ParseXslt(this.root);
let value = result.DescendantsAndSelf() |> Seq.find(fun x -> x.Name = XName.Get "ROOT")
Assert.AreEqual(" Text To Replace ", value.Value)
Those [<DefaultValue>] val mutable to declare the variables (without initializing because that's SetUp job) and make those variables available to all the class scope, and the fact that I've basically done a 1:1 translation from what I had in C# without any apparent gaining in syntax and readability gave me the chills. Is there any way to rewrite these kind of tests and setups that looks nicer? Because all examples I've seen all over internet are simple, small and do not cover these cases.
Let's reduce the problem to a more manageable size first:
Reduced problem
In this test, you have two mutable fields being initialized in the SetUp method:
[<TestFixture>]
type MutableTests() =
[<DefaultValue>] val mutable foo : int
[<DefaultValue>] val mutable bar : int
[<SetUp>]
member this.SetUp () =
this.foo <- 42
this.bar <- 1337
[<Test>]
member this.TheTest () =
Assert.AreEqual(42, this.foo)
Assert.AreEqual(1337, this.bar)
Obviously, this is a stand-in for the real problem.
Functions that return values
Instead of setting class fields, why not write functions that initialize the values that you need?
module BetterTests =
let createDefaultFoo () = 42
let createDefaultBar () = 1337
[<Test>]
let ``a test using individual creation functions`` () =
let foo = createDefaultFoo ()
let bar = createDefaultBar ()
Assert.AreEqual(42, foo)
Assert.AreEqual(1337, bar)
If you wan't all the values at once (like you have access to all fields from within a class), you can define a single function that returns all values in a tuple or record:
let createAllDefaultValues () = createDefaultFoo (), createDefaultBar ()
[<Test>]
let ``a test using a single creation function`` () =
let foo, bar = createAllDefaultValues ()
Assert.AreEqual(42, foo)
Assert.AreEqual(1337, bar)
This example uses an int * int tuple, but it might be more readable to define a record:
type TestValues = { Foo : int; Bar : int }
let createDefaultTestValues () = {
Foo = createDefaultFoo ()
Bar = createDefaultBar () }
[<Test>]
let ``a test using a single creation function that returns a record`` () =
let defaultValues = createDefaultTestValues ()
Assert.AreEqual(42, defaultValues.Foo)
Assert.AreEqual(1337, defaultValues.Bar)
Notice that, unlike classes in C#, records in F# are super-lightweight to declare.
If you want to learn more about idiomatic unit testing with F#, a good place to start could be my Pluralsight course about unit testing with F#.
I work with Play! Scala 2.2 and I need to make recursive web services call (until I get all the results).
For the moment I managed to make it with Await.result as following :
def getTitleSetFromJson( jsValue: JsValue ): Set[ String ] = {
val titleReads: Reads[Option[String]] = (__ \\ "title").readNullable[String]
(jsValue \ "response" \ "songs")
.asOpt[Set[Option[String]]](Reads.set(titleReads))
.getOrElse(Set.empty)
.flatten
}
def findEchonestSongs(start: Long, echonestId: String): Future[Set[String]] = {
val titleReads: Reads[Option[String]] = (__ \\ "title").readNullable[String]
val endpoint = s"$baseUrl/artist/songs"
val assembledUrl = s"$endpoint?api_key=$echonestApiKey&id=$artistId&format=json&start=$start&results=20"
WS.url(assembledUrl).get().map { songs =>
if ((songs.json \ "response" \ "total").asOpt[Int].getOrElse(0) > start + 20) {
getTitleSetFromJson(songs.json) ++
Await.result( findEchonestSongs(start + 20, echonestId), 3.seconds )
} else {
getTitleSetFromJson(songs.json)
}
}
}
Now I would like to make the same thing but with the non blocking way i.e. without using Await.result but with everything I tried I got errors : types mismatch because of the nested futures.
Could you please give me the way to do this?
There are many different ways to achieve your requirement. But maybe the simplest way is using flatMap.
Here is an improved version of your function (for making it simpler I took the liberty to make it return a Future[Set[JsValue]]).
import play.api.libs.json.JsValue
import play.api.libs.ws.WS
import scala.concurrent.Future
import play.api.Play.current
import scala.concurrent.ExecutionContext.Implicits.global
object Test {
val apiKey = "//YOUR_ECHONEST_API_KEY//"
val baseUrl = "http://developer.echonest.com/api/v4"
def findSongs(start: Long, artistId: String): Future[Set[JsValue]] = {
def endpoint = s"$baseUrl/artist/songs"
def assembledUrl = s"$endpoint?api_key=$apiKey&id=$artistId&format=json&start=$start&results=20"
def response = WS.url(assembledUrl).get()
def futureJson = response map (_.json)
futureJson flatMap { result =>
def total = (result \ "response" \ "total").asOpt[Int]
def songs = (result \ "response" \ "songs").as[Set[JsValue]]
total exists (_ > start + 20) match {
case false => Future.successful(songs)
case true => findSongs(start + 20, artistId) map (songs ++ _)
}
}
}
}
Working on the result
If you really want a Set[String] you can easily use map on the result.
val sample = findSongs(0, "someone")
sample.map(_.map(_ \ "title").map(_.as[String]))
Improvements
There is a lot of room for improvement here. For instance there is no need to pass the artistId every time you cal the function recursively. So we can define an inner function which is recursive.
It is not that difficult to make it tail recursive by using an accumulator.
Finally it might make sense to use a Seq[Future] and use fold instead of recursion, which is again a common pattern in functional programming. But all these stuff are already provided nicely by Stream api. You can refer to the Stream API for more info.
You can map the results from other ( from nested call ) future to be added to current Set.
Also... One very important thing. Readability Counts... Specially on StackOverFLow.
def findEchonestSongs(start: Long, echonestId: String): Future[Set[String]] = {
val titleReads: Reads[Option[String]] = (__ \\ "title").readNullable[String]
val requestUrl = "http://developer.echonest.com/api/v4/artist/songs?api_key=" + echonestApiKey + "&id=" + echonestId +
"&format=json&start=" + start + "&results=20"
WS.url( requestUrl ).get().flatMap { songs =>
var nTotal = (songs.json \ "response" \ "total").asOpt[Int].getOrElse(0)
if( nTotal > start + 20 ) {
val titleSet = getTitleSetFromJson( songs.json )
val moreTitleSetFuture = findEchonestSongs( start + 20, echonestId )
moreTitleSetFuture.map( moreTitleSet => titleSet ++ moreTitleSet )
}
else {
Future.successful( getTitleSetFromJson( songs.json ) )
}
}
}
def getTitleSetFromJson( jsValue: JsValue ): Set[ String ] = {
(songs.json \ "response" \ "songs")
.asOpt[Set[Option[String]]](Reads.set(titleReads))
.getOrElse(Set.empty)
.flatten
}