Bercriber's Blog

すべて 日記 音楽 短歌 振り返り プログラミング

2022/08/11 20:33


zigでinterface的なやつ

fieldParentPtrイディオムは極めてシンプルな実装になっている。しかしfieldParentPtrイディオムはLLVMの最適化と相性が悪いらしい。zig ver0.9からはAllocgateと呼ばれる手法でstd.mem.Allocatorやstd.rand.Randomが多相化されている。*anyopaqueを使って型を無視することで動的に実装を選択できるようになっている。ええんかそれで。標準ライブラリでそれやってるくらいならそのうちに言語機能にtraitなりinterfaceなり実装されそうな気がするが、これの構文糖みたいなもんになるのだろうか。

const std = @import("std");

/// Use @fieldParentPtr
/// https://zig.news/david_vanderson/interfaces-in-zig-o1c
const FooI = struct {
    fooFn: fn(*FooI) void,
    fn foo(self: *FooI) void {
        self.fooFn(self);
    }
};

const Bar = struct {
    sym: u8,
    fooI: FooI,
    fn init() Bar {
        return .{ .sym = 0xff, .fooI = FooI { .fooFn = fooFn } };
    }
    fn fooFn(fooI: *FooI) void {
        const self = @fieldParentPtr(Bar, "fooI", fooI);
        std.debug.print("foo({}) from Bar\n", .{self.sym});
    }
};

/// Allocgate
/// https://pithlessly.github.io/allocgate.html
/// https://github.com/ziglang/zig/blob/master/lib/std/mem/Allocator.zig
/// https://github.com/ziglang/zig/blob/master/lib/std/rand.zig
const HogeI = struct {
    ptr: *anyopaque,
    hogeFn: fn (ptr: *anyopaque) void,
    fn init(pointer: anytype, comptime hogeFn: fn (ptr: @TypeOf(pointer)) void) HogeI {
        const Ptr = @TypeOf(pointer);
        std.debug.assert(@typeInfo(Ptr) == .Pointer); // Must be a pointer
        std.debug.assert(@typeInfo(Ptr).Pointer.size == .One); // Must be a single-item pointer
        std.debug.assert(@typeInfo(@typeInfo(Ptr).Pointer.child) == .Struct); // Must point to a struct
        const gen = struct {
            fn hoge(ptr: *anyopaque) void {
                const alignment = @typeInfo(Ptr).Pointer.alignment;
                const self = @ptrCast(Ptr, @alignCast(alignment, ptr));
                @call(.{.modifier = .always_inline}, hogeFn, .{self});
            }
        };
        return .{
            .ptr = pointer,
            .hogeFn = gen.hoge
        };
    }

    fn hoge(self: HogeI) void {
        self.hogeFn(self.ptr);
    }
};

const Fuga = struct {
    sym: u8,
    fn init() Fuga {
        return .{
            .sym = 0xff / 2
        };
    }

    fn hogeI(self: *Fuga) HogeI {
        return HogeI.init(self, hogeFn);
    }

    fn hogeFn(self: *Fuga) void {
        std.debug.print("hoo({}) from Fuga\n", .{self.sym});
    }
};



pub fn main() !void {
    var bar = Bar.init();
    bar.fooI.foo();

    var fuga = Fuga.init();
    var hogeI = fuga.hogeI();
    hogeI.hoge();
}

2022/08/03 18:34


Zigでマンデルブロ集合

zigが流行ってると聞いて。人類に手動メモリ管理はまだ早いんだよな。もっと脳が進化してくれないと難しい。足りない脳でやってくしかないのだ現代人たる我(々?)は。

SDLのビルドでつまづいたりした。自分でbuild.zigいじるのいやだったので、パッケージマネージャーであるところのgyroを使ってみたけどうまく動かなかった。直接git cloneして/libsにでも突っ込んどけばなんとかなるっぽい。SDL2-develもルートに置いとけば勝手に見てくれるっぽい。SDL.zigがビルド用のSdkを用意してくれてるのでそれ任せ。

const std = @import("std");
const SDL = @import("sdl2");
const math = std.math;
const complex = math.complex;

pub fn main() !void {
    try SDL.init(.{
        .video = true,
        .events = true,
        .audio = true,
    });
    defer SDL.quit();

    const height = 800;
    const width = 800;

    var window = try SDL.createWindow(
        "SDL.zig",
        .{ .centered = {} },
        .{ .centered = {} },
        width,
        height,
        .{ .vis = .shown },
    );
    defer window.destroy();

    var renderer = try SDL.createRenderer(window, null, .{ .accelerated = true });
    defer renderer.destroy();

    var texture = try SDL.createTexture(renderer, .rgba8888, .streaming, width, height);
    defer texture.destroy();

    var buf = std.mem.zeroes([width * height * 4]u8);
    var y: f32 = -2;
    var x: f32 = -2;
    var i: usize = 0;
    while (i < width * height * 4) : (i += 4) {
        const m = mandelbrot(x, y, 255);
        const c = @intCast(u8, m);
        const b = @floatToInt(u8, @fabs(@floor(x * @intToFloat(f32, m))) / 255);
        buf[i + 0] = c;
        buf[i + 1] = b;
        buf[i + 2] = c;
        buf[i + 3] = c;

        x += 0.005;
        if ((i / 4) % width == 0) {
            x = -2;
            y += 0.005;
        }
    }

    try texture.update(&buf, 4 * width, null);

    mainLoop: while (true) {
        while (SDL.pollEvent()) |ev| {
            switch (ev) {
                .quit => {
                    break :mainLoop;
                },
                .key_down => |key| {
                    switch (key.scancode) {
                        .q => break :mainLoop,
                        else => {},
                    }
                },
                else => {},
            }
        }

        try renderer.setColorRGB(0, 0, 0);
        try renderer.clear();
        try renderer.copy(texture, null, null);
        renderer.present();
    }
}

fn mandelbrot(x: f32, y: f32, limit: u32) u32 {
    const c = math.Complex(f32).init(x, y);
    var z = math.Complex(f32).init(0.0, 0.0);
    var n: u32 = 0;
    while (n < limit) : (n += 1) {
        z = z.mul(z).add(c);

        if (complex.abs(z) > 2) {
            return n;
        }
    }
    return 0;
}

2022/07/31 14:34


Pop_OSインストールしたら夕暮れを壁を挟んで三回背にしてた

PopOSをインストールして環境設定してたらくそめんどくさかったんだけど、何一つメモ残してないから何がめんどくさかったのかわからないまま、ブログアップロードテストを迎えてしまったらいなう。

設定するだけしてやることない問題


2022/07/24 21:01


SP--してからwrite、readしてからSP++

GBEのPUSH/POPについてはそういうことみたいです。


2022/07/19 18:41


QMKにNICOLA配列でも設定するか

keymap

PreonicのQMKに定義してみた。一年ほど前に。キーマップ書いて一切練習してなかったのを思い出してちょっとやってみるかっと思ってファームウェア書き込んだら、修飾キーあたりが動かんくてなるほど、それで練習しなかったのかとなっとく。調べてみたもののこれでいいはずなんだけどなーってやつが思い通り動いてくれないので、NICOLAモード中にAltやCtrlを使えないという雑な作りでもういいや状態になってしまった。


とかなんとか書いていて、ふと思いついた修正をいれてみたら直ったぞ。まぁよかった。

Unicode Support

話を戻して、rules.mkUNICODEMAP_ENABLE = yesとしてUnicodeMapをわーと日本語の定義を書いて、ってこれぜったいどっかにあるだろ。自分で書くなよ。と思いつつも書いて。NICOLA配列もわーとならべて。いくぜーとフラッシュしても全然文字化けしてて。えーと思いつつぐぐると設定がもうひとつたりてなくて。UnicodeのInputModeを指定する必要があるらしく、しかしWindowsのビルドインメソッドはおすすめしないよと書いてあって、代わりに書いてあったのが、WinComposeというアプリを使えと。それインスコして、matrix_init_userset_unicode_input_mode(UC_WINC)を追加しておつかれさまでした。


2022/07/11 21:03


MX Ergo買い替えた

接続がよく切れるなーと思ってたらその日のうちに接続不可になって壊れたので買い替えた。M575でもいいかなとか思ったけどあちらは表面が樹脂のままなのですべるらしいし、傾きもたりない感じなので、そのままMXErgoを新しくしたらちゃんと接続できて、なんぞかんぞドライバーいじくちゃったせいかもなーとか思ってたけど大事には至らずにすんだ。2017年10月に買ったやつだったので、五年弱も使えたってことですばらしい。クリック音の静音化したやつも売ってくれないかなー。


2022/07/04 09:33


Monad/MonadState/MonadTrans/MonadIOのinstanceを定義する

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

data InnerState = InnerState { _y :: Int }
newtype Inner m a = Inner { runInner :: StateT InnerState m a }
  deriving (Functor,Applicative,Monad,MonadState InnerState,MonadTrans,MonadIO)

data AppState = AppState { _x :: Int }
newtype App m a = App { runApp :: StateT AppState m a }
  deriving (Functor,Applicative,Monad,MonadState AppState,MonadTrans,MonadIO)

GeneralizedNewtypeDerivingに任せれば良い。以下経緯。


data InnerState = InnerState { _y :: Int }
type Inner m a = StateT InnerState m a

data AppState = AppState { _x :: Int }
type App m a = StateT AppState (StateT InnerState m a) a

makeLenses ''InnerState
makeLenses ''AppState

app :: App IO ()
app = do
  x += 1
  x' <- use x 
  y' <- lift $ do
    y += 1
    use y
  liftIO $ print (x',y')

みたいなことをやりたいとして、とりあえず動くわけだがMonadスタックが深くなってくるとtypeの書き方が気になってくる。

type App m a = 
  StateT AppState (StateT InnerState (StateT InnerState2 (StateT InnerState3 (StateT InnerState4)))) a

このデータ構造の設計がそもそも悪いのではないかと言われればそうなんだが、まぁごりごりやっていくとこう成り果ててしまうこともある。それはそれとして。こういう場合にtypeの書き方を以下のように短くしたい。

type Inner m a = StateT InnerState m a
type Inner2 m a = StateT InnerState2 m a
type Inner3 m a = StateT InnerState3 m a
type Inner4 m a = StateT InnerState4 m a
type App m2 a = StateT AppState (Inner (Inner2 (Inner3 (Inner4 m)))) a

しかし、

--  • The type synonym ‘Inner’ should have 2 arguments, but has been given 1
--  • In the type synonym declaration for ‘App’
--
--  | type App m2 a = 
--  |   StateT AppState (Inner (Inner2 (Inner3 (Inner4 m)))) a

と怒られる。typeではうまくいかないようだ。そこでtypeをnewtypeにすると、

newtype Inner m a =  Inner  { runInner  :: StateT InnerState m a }
newtype Inner2 m a = Inner2 { runInner2 :: StateT InnerState2 m a }
newtype Inner3 m a = Inner3 { runInner3 :: StateT InnerState3 m a }
newtype Inner4 m a = Inner4 { runInner4 :: StateT InnerState4 m a }
newtype App m a = App {
    runApp :: StateT AppState (Inner (Inner2 (Inner3 (Inner4 m)))) a
  }

部分的には解決しそうだが、

--  • No instance for (Monad (App IO)) arising from a do statement
--  • No instance for (MonadState AppState (App IO))
--  • No instance for (MonadTrans App) arising from a use of ‘lift’
--  • No instance for (MonadState InnerState IO)
--  • No instance for (MonadIO (App IO)) arising from a use of ‘liftIO’

と怒られる。こんどはMonad系のinstanceがないぞと。こちらとしてはただのStateT HogeState m aのエイリアスみたいなノリで使いたいだけなのに自分で定義する必要があるらしい。

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- Inner[2,3,4]は省略
newtype Inner m a = Inner { runInner :: StateT InnerState m a }
  deriving (Functor,Applicative,Monad,MonadState InnerState,MonadIO,MonadTrans)

newtype App m a = App { runApp :: StateT AppState m a }
  deriving (Functor,Applicative,Monad,MonadState AppState,MonadIO,MonadTrans)

GNDのderivingが全部やってくれる。最初は以下のように手で書いていたがそんな必要はなくて俺はなにやってたんだ状態になったのでした。最初にGNDを試して駄目だった気がしたのは気のせいだったのだろう。

newtype Inner m a = Inner { runInner :: StateT InnerState m a }
  deriving (Functor,Applicative,Monad)

newtype App m a = App { runApp :: StateT AppState m a }
  deriving (Functor,Applicative,Monad)

instance Monad m => MonadState InnerState (Inner m) where
  get = Inner get
  put = Inner . put

instance Monad m => MonadState AppState (App m) where
  get = App get
  put = App . put

instance MonadTrans App where
  lift m = App $ StateT $ \s -> do
    a <- m
    pure (a,s)

instance MonadTrans Inner where
  lift m = Inner $ StateT $ \s -> do
    a <- m
    pure (a,s)

instance MonadIO m => MonadIO (App m) where
  liftIO = lift . liftIO

instance MonadIO m => MonadIO (Inner m) where
  liftIO = lift . liftIO

2022/06/26 17:37


ドラゴンライダーシリーズ

エラゴン、エルデスト、ブリジンガー、インヘリタンスと続いたドラゴンライダーシリーズを読み終えた。子供の頃エラゴンとエルデストを読み、友達と映画まで見に行った覚えがある。痛烈に覚えている景色があった。エルフの森の建物が木そのものと一体となっている景色だ。木造の家ではない。木の成長、形を魔法でコントロールして生きたままの植物が家になっている景色だ。さらにグレイダーが崖の下から羽ばたき現れた景色、マーダックに敗北する前に戦場上空をサフィラと共に疲れ切った様子で飛び回っている景色など、エルデストのワンシーンが大人になってからも時折頭に浮かんだりした。そのたびに残り二巻も読みたいと思ってはいたが、本の分厚さを思い出すとなかなか読み出せなかった。今年の目標としてドラゴンライダーシリーズを読むというのを立てていたせいか、なんとなく読書ベクトル的な慣性がついていたので、ためしにKindleで一巻を買って読んでみた。すると驚くほど簡単に読み進められたので勢いよく物語は進んでいった。Kindle版というのが良かったと思う。あの分厚い本を寝っ転がりながら片手て読めるわけがないがKindleなら難なくできる。Kindleで得られる物理的な読書体験の良さがこんなにもありがたいなんて知らなかった。そんなわけで二ヶ月かかってはいるがシリーズを読破した。「アラケイジアの物語」という外伝もあるのでそちらも近々読む。

物語のラストは最初から決まっていた。指輪物語のラストのようだった。


2022/06/17 16:55


WindowsTerminal/powershellでのstack/GHCの文字化け

という話。


app\Main.hs:36:27: error:
    <stderr>: commitAndReleaseBuffer: invalid argument (invalid character)

とかなんとか言われて動かない場合にターミナルの文字コードをUTF-8に変えようと、

chcp 65001

とするも、

app\Main.hs:27:8: error:
    窶「 No instance for (Control.Monad.Reader.Class.MonadReader GB IO)
        arising from a use of 窶・iew窶・[0m
    窶「 In a stmt of a 'do' block: m <- view mem
      In the expression:
        do m <- view mem
           pure 10
      In an equation for 窶腕eadMem窶・
          readMem
            = do m <- view mem
                 pure 10
   |
27 |   m <- view mem
   |        ^^^^^^^^

みたいな感じで文字化けする場合、

[System.Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("utf-8")
[System.Console]::InputEncoding = [System.Text.Encoding]::GetEncoding("utf-8")
$env:LESSCHARSET = "utf-8"

とやると、

app\Main.hs:27:8: error:
    • No instance for (Control.Monad.Reader.Class.MonadReader GB IO)
        arising from a use of ‘view’
    • In a stmt of a 'do' block: m <- view mem
      In the expression:
        do m <- view mem
           pure 10
      In an equation for ‘readMem’:
          readMem
            = do m <- view mem
                 pure 10
   |
27 |   m <- view mem
   |        ^^^^^^^^

みたいな感じになっていい感じ。


2022/06/03 20:12


今日を生きている僕のせいで未来をまともに生きれない僕へ

うるせぇ静かにして暖かくして寝てろ。


第93期棋聖戦一局目で永瀬がイッヤッフー!!してるのでなかなか集中して書けない。


今を生きるだけで精一杯なのにどうしろと言うんだ。しかし10年20年先の自分はもっと体力的に余裕が無いはずである。それに備え今からやれるだけのことをしないとジリ貧なのは自明。とは言え、タイトルはむしろ方便なのではないか。未来の僕を理由にして今現状を否定したい欲望の表現型なのではないか。仕事辞めたい。ただそれだけ。ではないが。現状を変えたくてしょうがない。仕事がいやでしょうがない。今から僕はなにをするんだろうか。何もしない。



29