ログ日記

作業ログと日記とメモ

GHCJSを使えるようにしたログ

github.com

ソースからのインストールは出来たんだけど、それをstackで使う方法が分からなかった。
エラーがややこしいので元々入っているcabalを消したりhaskell-platformを消したりしてstackだけ動いている状態にして試した。
コンパイルが1時間近くかかる。長い。


取り敢えず動いたのはいいんだけど、emacsで不備が出る。stackで動かしたい。

https://docs.haskellstack.org/en/stable/ghcjs/#ghcjs
ここからのリンクの
https://github.com/matchwood/ghcjs-stack-dist
この設定をコピペして使う。

jsのビルドはスムーズになった。
だがやっぱり普通のHaskellのソースを書くようには行かない。


どうやらemacsのinteroが動かない。
github.com
closeになっているけどghcjs-base-stubもよく分からなかった。ghcjs --interactiveが無理だと厳しい?


よく考えたら素のHaskellが動くし
http://hackage.haskell.org/package/ghcjs-dom
この辺のGHCJS用のライブラリは普通のGHCで動くんだから、普通のGHCコンパイルし直してプログラミング中はHaskellプログラムとしておけば良さそうに思える。


コンパイルコンパイルコンパイル…長い。
github.com
そしてエラーが出る。
jsaddleのコンパイルでメモリ8GB以上持っていかれる。富豪的過ぎる…。
メモリを15GBにしてもスワップも持って行かれた。でも何とかコンパイル成功。

適当にsrcディレクトリをシンクしてGHCJS用のstackファイルとGHC用のstackファイルを用意して、ソースを書くときはGHC用のディレクトリで作業するようにすれば、何とか出来そうだった。



全体的に参考にしたのはこの辺。
qiita.com
labs.spookies.co.jp




それでstackの初期状態 "someFunc" を出力するプログラムをコンパイルしたらjsが1MBある。Closure Compilerで圧縮しても240Kある。
まあHaskellのbaseとかの環境が全部入っているとそうなるのか。Closure Compilerで未使用関数だと判別できない部分が多かったっぽい。


ちなみにClosure Compilerは

java -jar $DIR/compiler.jar \
     --compilation_level=ADVANCED_OPTIMIZATIONS \
     --define 'goog.LOCALE=ja' \
     --define 'goog.DEBUG=false' \
     --jscomp_off=checkVars \
     $GHCJSOUT \
     > $COMPILEDJS

こんな感じで、 --jscomp_off が必要。普通にやると関数の多重定義でエラーになる。

emacs-purpose(window-purpose)でバッファを開くウィンドウを目的(モード、ディレクトリ)ごとに固定する

昨日の続き。
Emacsの使い方を変えようとして無理だった - ログ日記


似たような質問を発見した。
stackoverflow.com
この人は .c と .h でウィンドウを固定したかったみたい。

ここでは buffer-stack が挙げられている。
これも少し使ってみたけれど、まあその名の通りstackなので自分の目的とちょっと違う。操作もちょっと大変そう。

次のコメントでは、.hファイルの次のファイルを開くのはIDEからEmacsにbad idea を持ち込むことだとまで言われているな…。まあ自分の場合はファイル名の頭文字とtabで探すのでちょっと異なるのだが。



Buffer management - WikEmacs
wikiを眺めていると、See AlsoにKeep a certain kind of buffers in a window: Purposeという項目があることに気付いた。separate bufferやbuffer list groupingとかで見ていたのでスルーしていた。

emacs-purpose(window-purpose)で目的ごとにバッファリストの設定が出来るっぽい。
確かに、何のためにウィンドウ別の履歴が欲しいのかと言うと、ウィンドウごとにそれぞれ別の目的があるからで、こちらの方がより上位(?)の概念である。単に履歴を分割するよりはhtmlを編集しているときにC-x C-f でphpを開いたり C-x 0 で閉じたとしても、phpファイルを開いたらphp用の別のウィンドウで立ち上がってくれる方が有り難い。

何故誰もオススメしていないのか…。



Usage · bmag/emacs-purpose Wiki · GitHub
ここの最後にdesctop-save-modeにはデフォルトで対応していると書いている。
なかなか良い感じに思えるが、軽く使ってみると以下の二点で不満が出てきた。

・バッファ名ではなくてファイルのpathでルールを作りたい
・切り替え時にデフォルトを表示してほしい


なのでカスタマイズするelispを書いた。

;; overwrite
(defadvice purpose--buffer-purpose-name-regexp-1 (around path-regexp (buffer-or-name regexp purpose))
  "Add filename with regexp feature."
  (let* (
         (mode (unless (stringp regexp)
                 (car regexp)))
         (mode-match (if mode (eq (purpose--buffer-major-mode buffer-or-name) mode) t))
         (bufname (or (and (bufferp buffer-or-name)
                           (buffer-name buffer-or-name))
                      buffer-or-name)
                  )
         (regexps (if (stringp regexp)
                      regexp
                    (cdr regexp)))
         (target (if mode
                     (buffer-file-name (get-buffer bufname))
                   bufname))
         )

    (when (and mode-match target (string-match-p regexps target))
      (setq ad-return-value purpose)))
  )
(ad-activate 'purpose--buffer-purpose-name-regexp-1)

(defadvice purpose-read-buffers-with-purpose (around with-default (purpose))
  "Prompt with default."
  (let* (
         (other-buffer (delq (current-buffer)
                             (purpose-buffers-with-purpose purpose)))
         (default (or (buffer-name (car other-buffer))
                      "*scratch*"))

         (ret (completing-read
              (concat "[PU] Buffer (default " default "): ")
              (mapcar #'buffer-name
                      other-buffer)
              nil t nil))
         )
    (setq ad-return-value (if (string= ret "") default ret))
    ))
(ad-activate 'purpose-read-buffers-with-purpose)

設定のリストががっつりコアに書かれているので、うまくhookできなかった。なのでコアの関数のカスタマイズ。
引数がregexpじゃなくてコールバックだと良かったんだけれど。

文法を調べつつ書いていてあまり自信なし。
let*で逐次的に書いていてlispっぽくない気がする。


設定はこんなの。

(setq desktop-path '("."))
(desktop-save-mode t)


(require 'window-purpose)
(purpose-mode)

(require 'window-purpose-x)
(purpose-x-kill-setup)

(add-to-list 'purpose-user-mode-purposes '(php-mode . php))
(add-to-list 'purpose-user-mode-purposes '(web-mode . web))
(add-to-list 'purpose-user-mode-purposes '(js2-mode . js2))
(add-to-list 'purpose-user-mode-purposes '(css-mode . css))
(add-to-list 'purpose-user-mode-purposes '(sql-mode . sql))

(add-to-list 'purpose-user-regexp-purposes '((php-mode . "/Page/") . phppage))
(add-to-list 'purpose-user-regexp-purposes '((php-mode . "/Model/") . phpmodel))
(add-to-list 'purpose-user-regexp-purposes '((php-mode . "/Dto/") . phpdto))

(purpose-compile-user-configuration)

;; purpose-switch-buffer-with-purpose to default
(define-purpose-prefix-overload purpose-switch-buffer-overload
  '(purpose-switch-buffer-with-purpose
    switch-to-buffer
    ))


(global-set-key [S-right] 'split-window-horizontally)
(global-set-key [S-left] 'split-window-horizontally)
(global-set-key [S-up] 'split-window-vertically)
;(define-key global-map [S-down] 'delete-other-windows)


(global-set-key [right] 'windmove-right)
(global-set-key [left] 'windmove-left)
(global-set-key [up] 'windmove-up)
(global-set-key [down] 'windmove-down) 

取り敢えずPHPのモデル用のウィンドウ、画面用のウィンドウ、というように指定できるようにした。
major modeとセットなので、ディレクトリに別の拡張子のファイルがあったらそれは別のルールになるようにしている。




本当は .dir-locals.el に書きたかったんだけれど、こっちも書式がよく分からず取り敢えずinit.elに書いた。
やっと設定が出来てきたところなので使い勝手はまた今度。

Emacsの使い方を変えようとして無理だった

Emacsのウィンドウまたはフレームごとにバッファリストを分けたい。
色々調べたけれど無理そう。


stackoverflow.com
ここの3番目の答えにあるように、自分の使い方としては昔から

emacs &

で別々のEmacsを立ち上げていた。



例えばユーザーのマイページを作る場合、

emacs src/Page/MypageController.php &
emacs template/mypate.blade.php &
emacs css/mypage.css &

のように複数立ち上げて横に並べたい。
cssを編集していて、これは共通だなと思ったら 「C-x C-f common.css」としてcssのウィンドウで別cssのバッファを開く。
htmlを書いていて、これは共通メニューだなと思ったら「C-x C-f menu.blade.php」でhtmlのウィンドウで別のhtmlバッファを開く。
共通cssからmypage.cssに戻るときは、「C-x b RET」で戻る。


この何も考えずに機械的に戻る作業が、横分割のウィンドウだと出来ないんだよなあ…。
メニューに対応する menu.css を開いて3ファイル以上になった場合でも、バッファ一覧を見ずに「C-x b my TAB RET」で切り替える。
一覧から選ぶという作業をせずに、それでいて頭を使わずに補完したい。



ido, elscreen, elscreen-separate-buffer-list を入れると近いところまで行ったんだけど、なんか微妙に違う。
分割された一つのウィンドウの中でタブ切り替えしたいけど、タブはフレームで共通っぽい。ウィンドウ分割とタブ分割を両方使うと変な動きになる。
desktop-save-modeとの同時使用も動きが怪しい。


qiita.com
こんな感じでタブを消して、新タブ=新フレームみたいにしてウィンドウは一つでやれば良いのかもしれないが…うーん。


persp-modeも試したけどなんか動きが変。
spacemacsも試したら変わりすぎた。



detail.chiebukuro.yahoo.co.jp
動きがおかしい部分はこんなふうにサクッと修正できればいいんだけど…。

Closure Compiler の出力するJavaScriptを ES5(旧)とES6(新)で比べる

ふと、ES6よりES5で書いた方が軽くなるかと思ったので疑問を解消するために確かめる。
※ ES5、ES6と書きつつ、単に書き方の違いの調査になってきたので新・旧とする。



Closure Compiler と Closure Libraryをダウンロードする。

wget https://dl.google.com/closure-compiler/compiler-latest.zip

unar compiler-latest.zip
ln -s compiler-latest/closure-compiler-v20190325.jar closure-compiler.jar


https://github.com/google/closure-library/archive/v20190325.tar.gz
unar v20190325.tar.gz
ln -s closure-library-20190325 closure-library 

プログラムのディレクトリを作る。

mkdir js
mkdir js/es5
mkdir js/es6

Closure Compiler は何度も実行するのでコマンドを書いておく。
build.sh

#!/bin/bash


set -eux

function compile(){
    java -jar compiler.jar \
         --compilation_level=ADVANCED_OPTIMIZATIONS \
         --define 'goog.LOCALE=ja' \
         --define 'goog.DEBUG=false' \
         --create_source_map $1.js.map \
         --dependency_mode=STRICT \
         --entry_point=goog:entrypoint \
         'closure-library/closure/**.js' \
        '!closure-library/closure/**test.js' \
         "js/$1/**.js" \
         > $1.js

    echo >> $1.js
    echo "//@ sourceMappingURL=$1.js.map" >> $1.js
}

compile $1

まずは簡単なHello Worldから。

goog.provide('entrypoint');

function hello(v){
  console.log('hello ' + v);
}
function startApp(){
  hello('es5');
}

startApp();

goog.module('entrypoint');

const hello = (v) => console.log('hello ' + v);
const startApp = () => {
  hello('es6');
}

startApp();

まあこれぐらいなら基本なので全て削除されるかなと予想しつつコンパイル

chmld +x build.sh

./build.sh es5
./build.sh es6


結果。
app.js

console.log("hello es5");

console.log("hello es6");

ファイルを分けてみる。

// app.js
goog.provide('entrypoint');
goog.require('app.Foo');

window['startApp'] = app.Foo.startApp;

// foo.js
goog.provide('app.Foo');

app.Foo.hello = function(v){
  console.log('hello ' + v);
}
app.Foo.startApp = function(){
  app.Foo.hello('es5');
};

// app.js
goog.module('entrypoint');

const app = goog.require('app');
window['startApp'] = app.startApp;

// foo.js
goog.module('app');

const hello = (v) => console.log('hello ' + v);
const startApp = () => {
  hello('es6');
}

exports = {
  startApp
};


結果

window.startApp=function(){console.log("hello es5")};

window.startApp=function(){console.log("hello es6")};

どちらも無駄がない。



HTMLを作って呼び出す形にする。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Closure Test</title>

    <script src="es5.js"></script>
<!--    <script src="es6.js"></script> -->
  </head>
  <body>
    <h1>Closure Test</h1>
    <div id="contents">
      <div class="list"></div>
      <div class="list"></div>
      <div class="list"></div>
    </div>

    <script>
      startApp5();
      //startApp6();
    </script>
  </body>
</html>


// app.js
goog.provide('entrypoint');

goog.require('app.Foo');

window['startApp5'] = app.Foo.startApp;

// foo.js
goog.provide('app.Foo');
goog.provide('app.Bar');

goog.require('goog.dom');

app.Foo.hello = function(v){
  console.log('hello ' + v);
  
  var parentEl = goog.dom.getElement('contents');
  var children = goog.dom.getElementsByClass('list', parentEl);
  for (var i = 0; i < children.length; i++){
    goog.dom.setTextContent(children[i], 'list ' + i);
  }
}
app.Foo.startApp = function(){
  app.Foo.hello('es5');
};

// app.js
goog.module('entrypoint');

const app = goog.require('app');
window['startApp6'] = app.startApp;

// foo.js
goog.module('app');

const dom = goog.require('goog.dom');

const hello = (v) => {
  console.log('hello ' + v);

  const parentEl = dom.getElement('contents');
  const children = dom.getElementsByClass('list', parentEl);
  for (let i = 0; i < children.length; i++){
    dom.setTextContent(children[i], 'list ' + i);
  }
};
const startApp = () => hello('es6');


exports = {
  startApp
};


結果

var h = Array.prototype.indexOf ? function(a, b) {
    return Array.prototype.indexOf.call(a, b, void 0)
}
: function(a, b) {
    if ("string" == typeof a)
        return "string" == typeof b && 1 == b.length ? a.indexOf(b, 0) : -1;
    for (var c = 0; c < a.length; c++)
        if (c in a && a[c] === b)
            return c;
    return -1
}
;
window.startApp5 = function() {
    console.log("hello es5");
    var a, b = (a = document.getElementById("contents5")) || document;
    if (b.querySelectorAll && b.querySelector)
        a = b.querySelectorAll(".list");
    else {
        var c;
        b = document;
        a = a || b;
        if (a.querySelectorAll && a.querySelector)
            a = a.querySelectorAll(".list");
        else if (a.getElementsByClassName) {
            var d = a.getElementsByClassName("list");
            a = d
        } else {
            d = a.getElementsByTagName("*");
            var e = {};
            for (b = c = 0; a = d[b]; b++) {
                var g = a.className, f;
                if (f = "function" == typeof g.split)
                    f = 0 <= h(g.split(/\s+/), "list");
                f && (e[c++] = a)
            }
            e.length = c;
            a = e
        }
    }
    for (b = 0; b < a.length; b++)
        a[b].innerHTML = "list " + b
}
;

これもどちらも同じなので片方は省略。

goog.domを使ったのでindexOfやquerySelectorが無い場合の処理が入った。
querySelectorがある場合の処理だけ抜き出すと

window.startApp6 = function() {
    console.log("hello es6");
    var a, b = (a = document.getElementById("contents6")) || document;
    a = b.querySelectorAll(".list");
    }
    for (b = 0; b < a.length; b++)
        a[b].innerHTML = "list " + b
};

こうなる。
変数が再利用されていてvarとかletとかのレベルではなかった。




次にクラス。

// app.js
goog.provide('entrypoint');

goog.require('app.Bar');
goog.require('app.Baz');

var bar = new app.Bar('test');
bar.hello();
var baz = new app.Baz('test 2');
baz.hello();

// foo.js
goog.provide('app.Foo');
goog.provide('app.Bar');
goog.provide('app.Baz');

/**
 * @constructor
 */
app.Foo = function(value){
  this.value_ = value;
};
app.Foo.prototype.hello = function(){
  console.log('Foo > ' + this.value_);
};


/**
 * @constructor
 * @extends {app.Foo}
 */
app.Bar = function(value){
  app.Bar.base(this, 'constructor', value);
};
goog.inherits(app.Bar, app.Foo);

app.Foo.prototype.hello = function(){
  console.log('Bar > ' + this.value_);
};

/**
 * @constructor
 * @extends {app.Bar}
 */
app.Baz = function(value){
  app.Baz.base(this, 'constructor', value);
};
goog.inherits(app.Baz, app.Bar);

app.Foo.prototype.hello = function(){
  console.log('Baz > ' + this.value_);
};

// app.js
goog.module('entrypoint');

const app = goog.require('app');

const bar = new app.Bar('test');
bar.hello();
const baz = new app.Baz('test 2');
baz.hello();

// foo.js
goog.module('app');

class Foo {
  constructor(value){
    this.value_ = value;
  }

  hello(){
    console.log('Foo > ' + this.value_);
  }
}

class Bar extends Foo {
  constructor(value){
    super(value);
  }

  hello(){
    console.log('Bar > ' + this.value_);
  }
}

class Baz extends Bar {
  constructor(value){
    super(value);
  }

  hello(){
    console.log('Baz > ' + this.value_);
  }
}


exports = {
  Foo,
  Bar,
  Baz
}


結果

function c(a, d) {
    function g() {}
    g.prototype = d.prototype;
    a.f = d.prototype;
    a.prototype = new g;
    a.prototype.constructor = a;
    a.c = function(l, m, n) {
        for (var h = Array(arguments.length - 2), b = 2; b < arguments.length; b++)
            h[b - 2] = arguments[b];
        return d.prototype[m].apply(l, h)
    }
}
;function e(a) {
    this.a = a
}
e.prototype.b = function() {
    console.log("Foo > " + this.a)
}
;
function f(a) {
    this.a = a
}
c(f, e);
e.prototype.b = function() {
    console.log("Bar > " + this.a)
}
;
function k(a) {
    this.a = a
}
c(k, f);
e.prototype.b = function() {
    console.log("Baz > " + this.a)
}
;
(new f("test")).b();
(new k("test 2")).b();

var d = "function" == typeof Object.create ? Object.create : function(a) {
    function b() {}
    b.prototype = a;
    return new b
}
, e;
if ("function" == typeof Object.setPrototypeOf)
    e = Object.setPrototypeOf;
else {
    var f;
    a: {
        var g = {
            c: !0
        }
          , h = {};
        try {
            h.__proto__ = g;
            f = h.c;
            break a
        } catch (a) {}
        f = !1
    }
    e = f ? function(a, b) {
        a.__proto__ = b;
        if (a.__proto__ !== b)
            throw new TypeError(a + " is not extensible");
        return a
    }
    : null
}
var k = e;
function l(a, b) {
    a.prototype = d(b.prototype);
    a.prototype.constructor = a;
    if (k)
        k(a, b);
    else
        for (var c in b)
            if ("prototype" != c)
                if (Object.defineProperties) {
                    var m = Object.getOwnPropertyDescriptor(b, c);
                    m && Object.defineProperty(a, c, m)
                } else
                    a[c] = b[c];
    a.f = b.prototype
}
;function n(a) {
    this.a = a
}
n.prototype.b = function() {
    console.log("Foo > " + this.a)
}
;
function p(a) {
    this.a = a
}
l(p, n);
p.prototype.b = function() {
    console.log("Bar > " + this.a)
}
;
function q(a) {
    this.a = a
}
l(q, p);
q.prototype.b = function() {
    console.log("Baz > " + this.a)
}
;
(new p("test")).b();
(new q("test 2")).b();

class構文の方はトランスパイル後の共通コードが若干複雑になっているような。
ただし本体のコードはやはり同じだった。





その他、細かい動作等。
よくあるスコープミスのコード

function app()
{
  var a = [1, 2, 3, 5, 8];
  var b = [];
  for (var i = 0; i < a.length; i++){
    b.push(function(){
      console.log(i);
    });
  }
  for (var j = 0; j < b.length; j++){
    b[j]();
  }
}
app();
// compile
(function() {
    for (var a = [1, 2, 3, 5, 8], b = [], c = 0; c < a.length; c++)
        b.push(function() {
            console.log(c)
        });
    for (a = 0; a < b.length; a++)
        b[a]()
}
)();

単純なコードのままだけれどバグっている。
letを使う

function app()
{
  var a = [1, 2, 3, 5, 8];
  var b = [];
  for (let i = 0; i < a.length; i++){
    b.push(function(){
      console.log(i);
    });
  }
  for (let i = 0; i < b.length; i++){
    b[i]();
  }
}
app();
// compile
(function() {
    for (var a = [1, 2, 3, 5, 8], c = [], b = {
        a: 0
    }; b.a < a.length; b = {
        a: b.a
    },
    b.a++)
        c.push(function(d) {
            return function() {
                console.log(d.a)
            }
        }(b));
    for (a = 0; a < c.length; a++)
        c[a]()
}
)();

forEachを使うと安全。

goog.require('goog.array');

function app()
{
  var a = [1, 2, 3, 5, 8];
  var b = [];
  goog.array.forEach(a, function(a, i){
    b.push(function(){
      console.log(i);
    });
  });
  for (var j = 0; j < b.length; j++){
    b[j]();
  }
}
app();
// compile
var f = Array.prototype.forEach ? function(a, b) {
    Array.prototype.forEach.call(a, b, void 0)
}
: function(a, b) {
    for (var e = a.length, d = "string" == typeof a ? a.split("") : a, c = 0; c < e; c++)
        c in d && b.call(void 0, d[c], c, a)
}
;
(function() {
    var a = [];
    f([1, 2, 3, 5, 8], function(e, d) {
        a.push(function() {
            console.log(d)
        })
    });
    for (var b = 0; b < a.length; b++)
        a[b]()
}
)();

ただし素のJSで使おうとすると危険。

function app()
{
  var a = [1, 2, 3, 5, 8];
  var b = [];
  a.forEach(function(a, i){
    b.push(function(){
      console.log(i);
    });
  });
  for (let i = 0; i < b.length; i++){
    b[i]();
  }
}
app();
// compile
(function() {
    var a = [];
    [1, 2, 3, 5, 8].forEach(function(d, c) {
        a.push(function() {
            console.log(c)
        })
    });
    for (var b = 0; b < a.length; b++)
        a[b]()
}
)();

[].forEach を直接書くと、それはトランスパイル後もそのままになっている。

a.forEach(function(a, i){

の箇所を

a.myforEach(function(a, i){

にするとコンパイルエラーになるから判別は出来ているんだけど。
直接arrayの関数を呼び出す場合はブラウザ対応を開発者に任せるということかな。




クラスやthisが入ると長くなるのかなと思いきや、そうでもない。

const PREFIX = 'prefix_';
const SUFFIX = '_suffix';

const plusPrefix = (v) => PREFIX + v;
const plusSuffix = function(v){
  return v + SUFFIX;
};

class Foo
{
  constructor(val){
    this.val_ = val;
  }

  setValue(val){
    this.val_ = val;
  }
  getValue(){
    const v1 = plusPrefix(this.val_);
    return plusSuffix(v1);
  }

}

const foo = new Foo('val');
console.log(foo.getValue());
// compile
console.log("prefix_val_suffix");

あれ?クラスは?
驚愕の圧縮率。



何度も使うと結果が変わる。

const PREFIX = 'prefix_';
const SUFFIX = '_suffix';

const plusPrefix = (v) => PREFIX + v;
const plusSuffix = function(v){
  return v + SUFFIX;
};

class Foo
{
  constructor(val){
    this.val_ = val;
  }

  setValue(val){
    this.val_ = val;
  }
  getValue(){
    const v1 = plusPrefix(this.val_);
    return plusSuffix(v1);
  }

}

const foo = new Foo('val');
console.log(foo.getValue());

const foo2 = new Foo('val 2');
console.log(foo2.getValue());

const foo3 = new Foo('val 3');
console.log(foo3.getValue());
// compile
function a(b) {
    this.a = b
}
console.log("prefix_" + (new a("val")).a + "_suffix");
console.log("prefix_" + (new a("val 2")).a + "_suffix");
console.log("prefix_" + (new a("val 3")).a + "_suffix");

それでも動的な部分以外は定数になっている。


setValueを呼び出しても結果が変わる。

const PREFIX = 'prefix_';
const SUFFIX = '_suffix';

const plusPrefix = (v) => PREFIX + v;
const plusSuffix = function(v){
  return v + SUFFIX;
};

class Foo
{
  constructor(val){
    this.val_ = val;
  }

  setValue(val){
    this.val_ = val;
  }
  getValue(){
    const v1 = plusPrefix(this.val_);
    return plusSuffix(v1);
  }

}

const foo = new Foo('val');
foo.setValue('val2');
console.log(foo.getValue());
// compile
var a = new function() {
    this.a = "val"
}
;
a.a = "val2";
console.log("prefix_" + a.a + "_suffix");

なんかこのコンパイル結果を再びコンパイルすると綺麗に消せそうだけれども。
一度しか利用されていない変数やメソッドはインライン展開されるということかな。



クラス定数はどうか。

const PREFIX = 'prefix_';
const SUFFIX = '_suffix';

const plusPrefix = (v) => PREFIX + v;
const plusSuffix = function(v){
  return v + SUFFIX;
};

class Foo
{
  constructor(val){
    this.val_ = val;
  }

  setValue(val){
    this.val_ = val;
  }
  getValue(){
    const v1 = plusPrefix(this.val_);
    const v2 = plusSuffix(v1);
    const v3 = this.plusPrefix(v2);
    return Foo.plusSuffix(v3);
  }

  plusPrefix(v) { return this.CLASS_PREFIX + v; }
  get CLASS_PREFIX() { return 'classprefix_'; }

  static plusSuffix(v) { return v + Foo.CLASS_SUFFIX; };
  static get CLASS_SUFFIX() { return '_classsuffix'; }

}

const foo = new Foo('val');
console.log(foo.getValue());
// compile
var a = "undefined" != typeof window && window === this ? this : "undefined" != typeof global && null != global ? global : this;
function b(d) {
    this.b = d
}
a.Object.defineProperties(b.prototype, {
    a: {
        configurable: !0,
        enumerable: !0,
        get: function() {
            return "classprefix_"
        }
    }
});
a.Object.defineProperties(b, {
    a: {
        configurable: !0,
        enumerable: !0,
        get: function() {
            return "_classsuffix"
        }
    }
});
var c = new b("val");
console.log(c.a + ("prefix_" + c.b + "_suffix") + b.a);

なんか増えた。
素直にクラス外で定数にした方が良さそう。



効率良いトランスパイル結果を得ようと思ったら、 goog.module でモジュールにしつつthisを使わないシンプルな関数や定数の組み合わせでコードを書くのが良さそうだ。
まあそもそもthisを使うためにnewするのだから、thisを使わないのにクラスメソッドにするのは無駄な設計とも言えるね。

Kensington Expert Mouse を分解して断線を修理した

ExpertMouseが切れたりついたりして、仮想マシンの「どちらに接続しますなか?」が何度も出るようになったので直すことにする。
断線?というか接触不良的なやつ。
どうもコードがマウスに収納されていく部分が怪しくて、そこを持ってぐねぐね動かすとランプが激しく点滅する。

裏側4隅のネジを外す。
f:id:n314:20190331001438j:plain

ボールの下のネジを二箇所外すと取れる。
f:id:n314:20190331001542j:plain
かなり埃が溜まってたので左右の6箇所のネジを外して全部取って掃除した。


コードをうねうね動かした感じでは最初の想定通りの場所だったので、付近を切断。
f:id:n314:20190331001618j:plain
中に5本のコードがあるので全て接続。
なんか銅線?の他に繊維っぽい束もある。シールド的なこと?
また切れたら嫌なのではんだでがっつり接着した。
集中してたら写真撮るの忘れた。この写真は一通り終わってテープ巻いた後の状態。


カバー?的な入り口にハマってるやつは抜けなかったので諦めて別の場所に切れ込みを入れた。
適当に切ったけど丁度良いサイズで固定できた。

コードを引っ張っても点滅しなくなったので取り敢えず一安心。

フラッシュセッションにハマる

前にも同じ問題でハマったのに忘れていて再びハマったメモ。


リダイレクト前にセッションに値を保存して、リダイレクト後のページで取り出して、削除するような一度だけ使うセッション変数にはフラッシュセッションを使う。
セッション系ライブラリには大抵 flash メソッドがあると思う。
リロードにや更に次のページにも対応するために keep メソッドなんかも用意されている。

で、何故か値が消えていることがある。


詳しく処理を追うと、フロントコントローラーでファイルが存在しない場合は全てPHPが起動するようになっていて、not foundでもPHPが実行される。
このときログインメニューやメッセージ用にnot foundページにも関わらずセッションを使っていたりすると、ここでフラッシュセッションがリセットされる。


画面遷移のどこかで画像などがnot foundになった場合に、一見正常フローのようでも「画像呼び出し=>ファイルがない=>phpでnot found用処理」となって見えない画面遷移とフラッシュセッションのリセットが動いていることになる。


よく考えれば当然のことなんだけれど、スマートな解決方法が思い付かない。
とりあえず404や500のときはkeepする、あるいはフラッシュセッションを消す処理を実行しない、というようなちょっとややこしいプログラムを書いた。

macOS Mojave にアップグレードしたらVMware Fusionがプチフリするようになった

High Sierra の時も問題があったので、今回も何か挙動が変になることがあるだろうなーと思っていたらやっぱり問題が起きた。


ブラウザの新規ウィンドウを立ち上げたり画面遷移しただけで5〜10秒ほどフリーズするようになった。



VMware Fusion 設定の詳細の「ハードウェアバッファリング」を自動から有効に変更したら直った。今のところは。
「自動」の挙動が変わったのか、以前は無効でも問題なかったのかは不明。


あとごくまれにマウスカーソルが一瞬巨大化する問題は残ってる。