I'm just starting to learn Elm. In this program I would like to update the screen with the mouse coordinates and arrow key state formatted in some output.
My plan was to create a record called Input and have that set with the Signals by function input. Then showGameInputs would use the Input record to get the values and combine them with some text to return an Element to main.
import Mouse
import Keyboard
-- Create a record named Input
type Input = { mouseX:Int, mouseY:Int, arrowUpdown:Int, arrowLeftRight:Int }
-- Combine Signals into Input type
input: Signal Input
input = Input <~ Mouse.x ~ Mouse.y ~ lift .y Keyboard.arrows ~ lift .x Keyboard.arrows
showGameInputs: Input -> Element
showGameInputs { mouseX, mouseY, arrowUpdown, arrowLeftRight } = plainText ("asdf" ++ show mouseX)
main: Signal Element
main = showGameInputs input
Here is the error:
[1 of 1] Compiling Main ( Functions.elm )
Type error on line 19, column 23 to 28:
input
Expected Type: Signal Input
Actual Type: Input
Very new to Elm and functional programming so I suspect I am missing something fundamental here.
Thanks for any help.
You're almost there. The error message is a bit confusingly formatted (a known problem), but it says the type that input should actually be is Input. The reason it says that is because you're applying showGameInputs: Input -> Element on input: Signal Input. All you need is to change main to:
main = showGameInputs <~ input
Related
Got this:
for $config.IO.slurp.lines <-> $l {
$l .= trim;
...
}
Get this:
t/01-basic.rakutest ..3/5
Parameter '$l' expects a writable container (variable) as an argument,
but got '# karabiner config file' (Str) as a value without a container.
in sub generate_file at...
I've read the docs on containers but it didn't shed any light on what I can do in this situation aside from maybe assigning $l to a scalar variable, which seems hacky. Is there a way I can containerize $l?
The issue is really that .lines does not produce containers. So with <->, you would bind to the value, rather than a container. There are several ways to solve this, by containerizing as you suggested:
for $config.IO.slurp.lines -> $l is copy {
$l .= trim;
...
}
But that only makes sense if you want to do more changes to $l. If this is really just about trimming the line that you receive, you could do this on the fly:
for $config.IO.slurp.lines>>.trim -> $l {
...
}
Or, if you need to do more pre-processing $l, use a .map:
for $config.IO.slurp.lines.map({
.trim.subst("foo","bar",:g)
}) -> $l {
...
}
Maybe below is what you want? Generally, you read a file via slurp you can comfortably handle its size, or you read a file via lines if you want input taken in lazily, one-line-at-a-time:
my $config = 'alphabet_one_letter_per_line.txt';
my $txt1 = $config.IO.slurp;
$txt1.elems.say; #1
$txt1.print; #returns alphabet same as input
my $txt2 = $config.IO.lines;
$txt2.elems.say; #26
$txt2.join("\n").put; #returns alphabet same as input
Above, you get only 1 element when slurping, but 26 elements when reading lines. As you can see from the above code, there's no need to "...(assign) $l to a scalar variable..." because there's no need to create (temporary variable) $l.
You can store text in #txt arrays, and get the same number of elements as above. And you can just call routines on your stored text, as you have been doing (example below continues $txt2 example above):
$txt2.=map(*.uc);
say $txt2;
Sample Output:
(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
[Note, this question seems to have triggered questions on the use of $txt2.=map(*.uc); versus $txt2.=uc;. My rule-of-thumb is simple: if the data structure I'm working on has more than one element, I map using * 'whatever-star' to address the routine call to each element].
https://docs.raku.org/
The Elm Compiler gives me this error in my view function ...
TYPE MISMATCH - Something is off with the 2nd branch of this case expression:
div [] [
div [] [text (String.fromInt value)],
div [] [button [ onClick identity ] [ text "Enter number" ]]]
This div call produces:
Html #(a -> a)#
But the type annotation on view says it should be:
Html #Msg#Elm
Does anyone know how I can fix this issue?
Thank you!
module Main exposing (..)
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
-- MAIN
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model =
Result String Int
init : Model
init =
Result.Ok 0
-- UPDATE
type alias Msg = Int
update : Msg -> Model -> Model
update msg model =
collatz msg
collatz : Int -> Result String Int
collatz start =
if start <= 0 then
Result.Err "Only positive numbers are allowed"
else
Result.Ok (collatzHelper start 0)
collatzHelper : Int -> Int -> Int
collatzHelper start counter =
if start == 1 then
counter
else if modBy 2 start == 0 then
collatzHelper (start // 2) (counter + 1)
else
collatzHelper (3 * start + 1) (counter + 1)
-- VIEW
view : Model -> Html Msg
view model =
case model of
Err err ->
div [] [text err]
Ok value ->
div [] [
div [] [text (String.fromInt value)],
div [] [button [ onClick identity ] [ text "Enter number" ]]]
The compiler error message, as always with Elm, is fairly straightforward to understand. The problem is the onClick attribute in this line:
div [] [button [ onClick identity ] [ text "Enter number" ]]
As the compiler says, this has type a -> a, when it's expected to have type Msg, which in your case is the same as Int. That is, you've passed a function and it should be an integer.
Since value here is indeed an Int, that seems the natural choice to use here:
div [] [button [ onClick value ] [ text "Enter number" ]]
and this does indeed compile. However, this app doesn't seem particularly useful, because the user has no way to change the value displayed. You will need to include some sort of numeric input in your app to allow that - and then you will need a new alternative in your Msg type to handle the change of value.
Judging by this and your previous question (which is where I assume you got the idea of using identity as an onClick value - unfortunately that doesn't work in general), you seem to be struggling a bit with how the Elm Architecture works, so if you haven't already I would strongly recommend going through the tutorial.
A newbie for x3... The code is adapted from the roman.cpp in the x3 tutorial. Suppose I have a symbol table like below:
struct car_models_ : x3::symbols<char, unsigned>
{
car_models_()
{
add
("sedan", 1)
("compact", 2)
("suv", 3)
;
}
} car_models;
Then parse,
char const *first = "Model: sedan";
char const *last = first + std::strlen(first);
parse(first, last, "Model: " >> car_models[action()]);
If there is new model not listed in the symbol table, what would be the right way to handle it? Is there a way to add a wildcard as the last entry in the symbol table, and then somehow pass an unknown model to action (e.g., number "0" in this case)?
There is no way to do it inside the symbol table itself. One possibility is:
auto ext_car_models = car_models | (x3::omit[*x3::lower] >> attr(0))
Then to parse:
parse(first, last, "Model: " >> ext_car_models[action()]);
Ignoring the attribute for a moment, your symbol table is effectively syntactic sugar for:
x3::string("sedan") | "compact" | "suv"
So, handling an unknown string in that position would need to be handled the same way. You will need to define a parser that defines what a model string looks like. Possibly *x3::lower
I'm trying to make a text field that submits its input by pressing Enter and is cleared by Escape.
import Graphics.Element exposing (flow, down, show)
import Signal exposing ((<~), (~))
import Graphics.Input.Field exposing (noContent, defaultStyle, field)
import String
import Keyboard
main = Signal.map view (Signal.foldp step init signals)
signals = (,,) <~ box.signal
~ Signal.sampleOn Keyboard.enter box.signal
~ escape
escape = Keyboard.isDown 27
box = Signal.mailbox noContent
init = (noContent, noContent, False)
view (text, query, clear) =
flow down
[ field defaultStyle (Signal.message box.address) "Enter text" text
, show (String.words query.string)
]
step (text, query, clear) _ =
case clear of
False -> (text, query, clear)
True -> (noContent, noContent, clear)
This produces an empty field only while holding Escape and reverts to whatever was entered when Escape is released.
Trying to understand why this is the case lead me to a smaller example:
import Graphics.Element exposing (show)
import Signal
import Char
import String
import Keyboard
main = Signal.map show input
input = Signal.foldp step "" (Signal.map2 (,) Keyboard.presses escapeDown)
escapeDown = Keyboard.isDown 27
step (keyCode, esc) string =
case esc of
True -> ""
False -> string ++ keyToString keyCode
keyToString = String.fromChar << Char.fromCode
The accumulated string of characters is emptied on pressing Escape but releasing it results in a string of a single (last entered) character.
From what I understand, Keyboard.isDown signal is triggered while holding and on key release. So how can I clear the field persistently?
The reason you see this behaviour
When you create a signal of pairs (Signal (KeyCode,Bool)) out of two signals (Signal KeyCode/Signal Bool), that signal of pairs will update every time of of the signals update. So the value of Signal.map2 (,) Keyboard.presses escapeDown over time might be:
(0,False), (97,False), (98,False), (98,True), (98,False)
^ ^ press 'a' ^ press 'b' ^ ^ release escape
program start press escape
When you press escape, the pair of values changes and your foldp updates to the empty string. When you release escape the pair of values changes again, so foldp updates again, finds the False value, therefore appends the last character you pressed to the empty string.
Solution
In this case you're really interested in the event when escape is pressed, but not when it isDown. Instead of creating a pair of the signals, it is in this case better to merge the signals. To do so they need to be of the same type. Here's an example:
import Graphics.Element exposing (show)
import Signal exposing ((<~))
import Char
import String
import Keyboard
type Input = KeyPress Keyboard.KeyCode | EscapePress
main = Signal.map show output
presses = KeyPress <~ Signal.filter ((/=) 27) 0 Keyboard.presses
escapePress = always EscapePress <~ escapeDown
input = Signal.merge escapePress presses
output = Signal.foldp step "" input
escapeDown = Keyboard.isDown 27
step input string =
case input of
EscapePress -> ""
KeyPress keyCode -> string ++ keyToString keyCode
keyToString = String.fromChar << Char.fromCode
With a union type Input, we represent the different inputs to the program. The presses are wrapped in the KeyPress constructor of the union type. The escape button is represented with the other constructor EscapePress. Now you have two signals of the same type, which you can merge. In your step function you pattern match on the constructors of your Input, and handle the familiar cases.
Note that I'm filtering the Keyboard.presses signal so you don't get a KeyPress event from pressing down, holding down or letting go of the escape key.
I am creating a tokeniser in ML-Lex a part of the definition of which is
datatype lexresult = STRING
| STRINGOP
| EOF
val error = fn x => TextIO.output(TextIO.stdOut,x ^ "\n")
val eof = fn () => EOF
%%
%structure myLang
digit=[0-9];
ws=[\ \t\n];
str=\"[.*]+\";
strop=\[[0-9...?\^]\];
%s alpha;
alpha=[a-zA-Z];
%%
<alpha> {alphanum}+ => (ID);
. => (error ("myLang: ignoring bad character " ^ yytext); lex());
I want that the type ID should be detected only when it starts with or is found after "alpha". I know that writing it as
{alpha}+ {alphanum}* => (ID);
is an option but I need to learn to use the use of start states as well for some other purposes. Can someone please help me on this?
The information you need is in the documentation which comes with SML available in various places. Many university courses have online notes which contain working examples.
The first thing to note from your example code is that you have overloaded the name alpha and used it to name a state and a pattern. This is probably not a good idea. The pattern alphanum is not not defined, and the result ID is not declared. Some basic errors which you should probably fix before thinking about using states - or posting a question here on SO. Asking for help for code with such obvious faults in it is not encouraging help from the experts. :-)
Having fixed up those errors, we can start using states. Here is my version of your code:
datatype lexresult = ID
| EOF
val error = fn x => TextIO.output(TextIO.stdOut,x ^ "\n")
val eof = fn () => EOF
%%
%structure myLang
digit=[0-9];
ws=[\ \t\n];
str=\"[.*]+\";
strop=\[[0-9...?\^]\];
%s ALPHA_STATE;
alpha=[a-zA-Z];
alphanum=[a-zA-Z0-9];
%%
<INITIAL>{alpha} => (YYBEGIN ALPHA_STATE; continue());
<ALPHA_STATE>{alphanum}+ => (YYBEGIN INITIAL; TextIO.output(TextIO.stdOut,"ID\n"); ID);
. => (error ("myLang: ignoring bad character " ^ yytext); lex());
You can see I've added ID to the lexresult, named the state ALPHA_STATE and added the alphanum pattern. Now lets look at how the state code works:
There are two states in this program, they are called INITIAL and ALPHA_STATE (all lex programs have an INITIAL default state). It always begins recognising in the INITIAL state. Having a rule <INITIAL>{alpha} => indicates that if you encounter a letter when in the initial state (i.e. NOT in the ALPHA_STATE) then it is a match and the action should be invoked. The action for this rule works as follows:
YYBEGIN ALPHA_STATE; (* Switch from INITIAL state to ALPHA_STATE *)
continue() (* and keep going *)
Now we are in ALPHA_STATE it enables those rules defined for this state, which enable the rule <ALPHA_STATE>{alphanum} =>. The action on this rule switch back to the INITIAL state and record the match.
For a longer example of using states (lex rather than ML-lex) you can see my answer here: Error while parsing comments in lex.
To test this ML-LEX program I referenced this helpful question: building a lexical analyser using ml-lex, and generated the following SML program:
use "states.lex.sml";
open myLang
val lexer =
let
fun input f =
case TextIO.inputLine f of
SOME s => s
| NONE => raise Fail "Implement proper error handling."
in
myLang.makeLexer (fn (n:int) => input TextIO.stdIn)
end
val nextToken = lexer();
and just for completeness, it generated the following output demonstrating the match:
c:\Users\Brian>"%SMLNJ_HOME%\bin\sml" main.sml
Standard ML of New Jersey v110.78 [built: Sun Dec 21 15:52:08 2014]
[opening main.sml]
[opening states.lex.sml]
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[autoloading done]
structure myLang :
sig
structure UserDeclarations : <sig>
exception LexError
structure Internal : <sig>
val makeLexer : (int -> string) -> unit -> Internal.result
end
val it = () : unit
hello
ID