Posted at

Elmをやってみたログ

前から気になっていたのでやってみた。

疑問はあまり解決しないまま。

まずはエディタの設定。

elm-modeに加えてflycheckelm-oraclelsp-modeなどを試してみたけど動かない。

仕方がないので取り敢えずelm-modeだけでやってみた。

C-c C-cはダメっぽい。

https://emacs.stackexchange.com/questions/50479/elm-mode-cant-compile-file

色々大きく変更する必要があるっぽい。

C-c C-lはファイル名と場所を正しくすれば動くけど、アクティブウィンドウが切り替わるのが使いづらい。

https://emacs.stackexchange.com/questions/7409/is-there-a-generic-toggle-previous-window-function

ここに書いてある設定を使って

(defun switch-to-last-window ()

(interactive)
(let ((win (get-mru-window t t t)))
(unless win (error "Last window not found."))
(let ((frame (window-frame win)))
(raise-frame frame)
(select-frame frame)
(select-window win))))

(defun elm-repl-load-back ()
(interactive)
(elm-repl-load)
(switch-to-last-window)
)

(require 'elm-mode)
(add-hook 'elm-mode-hook
(lambda ()
;; (elm-format-on-save-mode)
(local-set-key (kbd "C-c C-l") 'elm-repl-load-back)
))

こんな感じにして、C-c C-lでアクティブウィンドウを切り替えないようにした。もっと単純な設定がある気がするが。

マニュアルを見ながら書いていく。

main =

Browser.element
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}

init = undefined
view = undefined
update = undefined
subscriptions = undefined

というようにまずはコンパイルを通すようにしてC-c C-lをカチカチしながら進めたいんだけど、Elmだとundefinedが無い?少しずつ書くにはどうしたらいいんだろう。

分からないのでそれも置いておいて、 https://github.com/evancz/elm-architecture-tutorial この辺を適当にいじってみる。

https://qiita.com/arowM/items/dfb38d1c5f3dfde8b8bf

この辺を読んでいると、どうも合ってない気がするが…。HTMLは普通にファイルで書きたいので、The Elm Architecture じゃなくなるよね。

例えば Closure Toolsを使いつつ

goog.provide('app.main');

goog.require('goog.dom');
goog.require('ElmJs');

function runQ(){
var q = window['q'] || [];
for (var i = 0; i < q.length; i++){
q[i]();
}
window['q'] = {
push: function(f){
f();
}
};
}

function runApp()
{
var el = goog.dom.getElement('elm');
console.log(el);

var app = window['Elm']['Main']['init']({
node: document.getElementById('elm')
});

}

window['runApp'] = runApp;
runQ();

こんな感じにしたい。Closure Compilerでまとめて圧縮しても一応動くけど、HTMLはサーバーサイドで書いたものをそのまま使いたいんだよなー…。

ていうわけで

https://qiita.com/ymtszw/items/d195c098445476527539

この辺を参考にしつつ

port module Main exposing (..)

import Platform
import Task
import Time
import Json.Encode as Encode
import Time.Extra

main =
Platform.worker
{ init = init
, update = update
, subscriptions = subscriptions
}

-- MODEL

type alias Model =
{ zone : Time.Zone
, time : Time.Posix
}

init : () -> (Model, Cmd Msg)
init _ =
( Model Time.utc (Time.millisToPosix 0)
, Task.perform AdjustTimeZone Time.here
)

-- UPDATE

type Msg
= Tick Time.Posix
| AdjustTimeZone Time.Zone

dateToStr : Model -> String
dateToStr model =
let
hour =
String.fromInt (Time.toHour model.zone model.time)
minute =
String.fromInt (Time.toMinute model.zone model.time)
second =
String.fromInt (Time.toSecond model.zone model.time)
in
hour ++ ":" ++ minute ++ ":" ++ second

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Tick newTime ->
let newmodel = { model | time = newTime }
in
( newmodel
, sender (Encode.string <| dateToStr newmodel)
)

AdjustTimeZone newZone ->
let newmodel = { model | zone = newZone }
in
( newmodel
, sender (Encode.string <| dateToStr newmodel)
)

-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg
subscriptions model =
Time.every 1000 Tick

port sender : Encode.Value -> Cmd msg

<!DOCTYPE HTML>

<html>
<head>
<meta charset="UTF-8">
<title>Main</title>
<script src="main.js"></script>
</head>

<body>
<h1>test</h1>
<div id="elm"></div>
<script>
var app = Elm.Main.init();
app.ports.sender.subscribe(
function(a){
document.getElementById('elm').innerHTML = a;
}
);
</script>
</body>
</html>

こんなコードをテストしてみたり。

検索して出てくるPlatform.programworkerに変わった?

動くけど、こんな使い方をしていいものかどうか。

理想は

https://qiita.com/arowM/items/6c32db1f9e4b92445f3b

ここに書いてあるようなパーサー等を使いつつ、HTMLやCSSは普通に別ファイルで書きたい。

https://n314.hatenablog.com/entry/20110824/1314182177 昔自分がScalaで書いたやつ。こんな感じにタグのclassやnameを読み取って自動でバインディングしたい。

可能かどうかで言えば可能な気がするし他の言語でやるよりやりやすい気がするけど、用途がズレているのが気になる。

あと気になるのが、PHPでParsecを書いたときは超遅くなってメモリも大量に使ったんだけど、JavaScriptは大丈夫なのかね。

トランスパイル時に良い感じに最適化してくれるんかな。