サクサク読めて、アプリ限定の機能も多数!
トップへ戻る
ドラクエ3
zenn.dev/qnighy
本稿では、Proxyを使う際はオブジェクトの原理をよく理解した上で、PreventExtensions時の挙動とPrivate Identifierに気をつけましょうという話をします。 そもそもProxyは何なのか Proxy は Reflect と対をなすプリミティブAPIで、オブジェクトの低レベルプロトコルの操作を提供します。 Proxyは、オブジェクトの低レベルプロトコルをユーザーが実装できるようにします。 Reflectは、オブジェクトの低レベルプロトコルをユーザーが利用できるようにします。 そのため、Proxyを理解するにはまずオブジェクトの低レベルプロトコルを理解する必要があります。 オブジェクトとは何か { foo: "bar" } や [1, 2, 3], () => 42 がオブジェクトであることはすでに知っていると思いますが、ここでは定義に戻って確認をします。 オブジェ
WebAssemblyをちょっといじってみて思ったところをまとめてみます。 設計思想 WebAssembly/designに設計文書がまとまっています。特にHighLevelGoals.mdから読み取れるポイントは以下の4点です。 サンドボックス化された環境であること。 移植性があること。つまり、特定の実CPUアーキテクチャ等に依存しないこと。 少なくともC/C++の(十分に高速な)コンパイルターゲットとして機能すること。 安定した仕様を持つこと。 サンドボックスという観点からは、先行技術として以下のようなものが特筆に値します。 Webサンドボックス JavaScript および asm.js Javaアプレット Flash (ActionScript) NaCl, PNaCl Web以外のサンドボックス OSのユーザーランド、特にLinux userland これらのサンドボックスとの比
デザインパターンライブラリを作った JSRの話だけ読みたい人は読み飛ばしてもOKです。 JavaScriptのtry-catchはC++の影響を受けており、以下の特徴があります。 (A) throwは大域脱出的である。 (B) try-catchはブロック内の全ての例外副作用に対して一括で作用する。 (C) try-catchは文であり、値を返せない。 (D) TypeScriptにおいて、例外型は明示されない。 このうち (B), (C), (D) の問題を解決するため、RustのResultや類似のパラダイムをJSに輸入する試みがしばしば行われています。しかしこの解決手段にはいくつかの問題があり、 (E) rethrowの専用構文がないためボイラープレートが増える。 (F) 出力ストリームに対するwriteなど、戻り値を持たない副作用関数に対するエラーハンドリングが抜け落ちないようにL
以下のページに主要ブラウザのuser agent stylesheetへのリンクがまとまっています。 しかし、2024年2月時点で、これらは最新リビジョンへのリンクを参照していません。以下が現在の正しい最新版URLです。 Chromium (Chrome) のuser agent stylesheet 奇妙なことに、Chromiumのソースツリーの third_party/blink 以下の内容は、フォルダ名に反し、Blinkの正式な最新版のようです。 GitHub上の公式ミラーからもアクセスできます。 WebKit (Safari) のuser agent stylesheet
all: unset; などを使ってUAスタイルシートを消してまっさらな場所からスタイルを当てるのは気持ちがいいですが、アクセシビリティ等の観点から重要な分岐が見落とされる可能性があります。 ここではChromeのUAスタイルシートを参考に、検討しておいたほうがいい状態をいくつかリストします。 (もちろん、既存のUIコンポーネントライブラリの使用が可能であれば、それが最も堅牢な選択肢でしょう。) 参考 各ブラウザのスタイルシート HTMLのスタイルシート UAスタイルの中には、CSSのカスケードルールの範疇で実装されているものもあれば、レンダリングエンジンの特別処理として書かれていて作者スタイルシートでの上書きが不可能なものもあります。これはブラウザ実装により異なります。 スコープ UIコンポーネントを作るような場面を想定しています。したがって、要素名自体は固定として、その中で見落としがち
課題 Rustでシンプルな単一化を書くことを考えます。単一化は主に型推論の実装に用いられます。 ここでは以下の方針で実装します。 一階の単一化。 変数は非負整数のidで表現し、0から順に付番する。 変数以外の項はRustのADTを使った通常の木構造で表現し、ノードの共有は行わない。 変数参照の縮約は行わない。 この方針のもと実装すると、およそ以下のようなコードになります。 #[derive(Debug, Clone)] pub struct UnifyEnv { vars: Vec<Option<Type>>, } #[derive(Debug)] pub struct UnificationFailure; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Type { Var(usize), Constr { constr: u32, ar
何を作ったか これを作りました。 解決しようとした課題 「小説家になろう」は公式に「小説Atom」というフィードを提供しており、小説の最新話を読むにはこれで事足ります。 しかし、すでに多くの話数が投稿されているストーリーを一気に読むのは大変なことがあります。 このような場合に、一度にまとめて読み進めるのではなく、一定のペースで少しずつ読み進めていく仕組みがあると便利だと考えました。 仕様 配信方法 やりたいことは「一定のペースで、過去のエピソードを配信する」ということです。 このような更新情報を提供するには古典的な仕組みとしてRSS/Atomがあります。RSS/Atomによる最新情報の取得は以前ほどは広くは使われていないように思えますが、今でもフィードリーダーは生き残っています。特定のSNSに連携するよりは汎用的な形式のほうが応用の幅は広くとれます。また、RSS/Atomであればpush通
TLSの有無 言うまでもないことですが、httpsでは通信路をTLSを使って保護することが想定されています。[1][2] デフォルポート httpは80、httpsは443です。[3][4] 権威性 以降の説明に入る前に前提を確認します。本稿は「httpとhttpsの違い」と題されていますが、これはURLのスキーム部分のことを指しています。URLはリソースの所在を指すものであり、通信方法はそこから二次的に決まるものです。このことを前提に置きつつ権威性について説明します。 Webにおいて、所望のリソースにアクセスする方法はひとつではありません。このような方法のうち、リソースの所有者の制御下にある(第三者による加工などが行われていないと期待される)方法で取得することを権威的アクセスと呼びます。[5] どのようなアクセス方法が権威的とみなせるかについて100%客観的で統一的な指標があるわけではな
何が大変か? VSCodeはMSが作っているだけあってTypeScript統合がほぼ整っており、特に設定を頑張らなくてもそのままIDE機能が効くことが多いです。現代の多くのJavaScriptプロジェクトはこれで事足ります。 しかし、これには例外があります。Reactの実装は現時点ではFlowで書かれています。FlowはTypeScriptと同様のコンセプトを持つAltJS言語です。FlowはReactと同じくMeta(Facebook)製であるため、これまでReactでFlowが使われてきたのは一定の合理性があると言えるでしょう。 Flowの拡張子が *.js であることから、TypeScriptのIDE機能はFlowのソースコードに対しても反応します。文法が似ていることもあり、Go To Definitionなどの機能もある程度までは動作しますが、完全ではありません。また、エラー表示が
export type Bookmark = { id: number; url: string; comment: string; }; このファイルには型しか書いてありませんね。ということは、「型定義ファイル」として bookmark.d.ts という名前にするべきでしょうか。実はそうではなく、この場合は bookmark.ts とするべきです。 「型定義ファイル」とは、「どこか別の場所にある実装に型をつけるためのファイル」です。たとえば、以下のファイルは「どこか別の場所にある実装」に型をつけているから、 *.d.ts にするのは自然です。 いっぽう、 type Bookmark は別のどこかにある *.js の型を与えているわけではないので、 *.ts でよいです。 このように本来 *.ts であるべきものを *.d.ts にしてしまうことには問題があります。代表的な問題として型エラ
Rustで値の文字列表現を返すにあたって、 String を直接返すのではなく Display を実装するのが一般的です。この派生パターンとして以下の4つのパターンを紹介します。 基本: 文字列化を実装したいとき 文字列化をインターフェースに含めたいとき カスタム文字列化 カスタム文字列化をインターフェースに含めたいとき 基本: 文字列化を実装したいとき Displayを実装すると、文字列化できるようになります。 println!, format! などのフォーマット処理から呼べるようになるほか、 .to_string() というヘルパ関数が使えるようになります。 以下はプログラミング言語処理系において、「変数」をあらわす構造体に文字列化を実装する例です。 Playground pub struct Var(String); impl std::fmt::Display for Var {
本稿の目的 Rustに存在しない「クラス」をRustの既存機能の組み合わせとして表現することで、一般的なOOP言語とRustのデータ表現に対する考え方の違いと、各概念がどのように対応しているかを理解しやすくすることが主な目的です。 主な想定言語 C++, Java, JavaScript, Rubyのクラスを主に想定しています。 方針 サブタイピング Rustにはごく限定的なサブタイピングしかないため、クラスのサブタイピングに相当する変換は明示的に .as_ref() / .as_mut() として表現します。 Deref / DerefMut を使うことで、このような振る舞いを部分的に再現できる場合もあります。ただし、この用途でDeref / DerefMutを使うのは推奨されていません。 カプセル化 一般的なOOP言語では継承関係に基づいたアクセス制御 (protectedなど) が行
型推論オプション 型推論の結果が変わるもの。 ⭐strict ... 以下のセット alwaysStrict strictNullChecks T | null や T | undefined が T に縮退しなくなる。 例 strictBindCallApply Function の各種メソッドが any に縮退しなくなる。 例 strictFunctionTypes コールバック関数の引数が共変でもunifyするようになる結果、型変数の推論優先度が変わることがある。 例 strictPropertyInitialization noImplicitAny 宣言型がない場合にflow typeが使われる機会が増える。 例 noImplicitThis thisの宣言型がない場合に文脈から型が決定される機会が増える。 例 useUnknownInCatchVariables catch (
dyno (RFC3192) はopen traitのための仕組み (言い換えると、trait downcastingの仕組み) をライブラリレベルで実現する提案です。 できることのイメージ 例として、以下のようなトレイトを考えます。 (std::error::Error を説明のために簡略化したものです) // エラー型はこれを実装する pub trait Error { // エラーの文字列表現を取得する fn to_string(&self) -> String; } これを拡張可能トレイトにするのが本RFCの目的です。 実際の実装はライブラリレベルで行われていますが、わかりやすくするために「拡張構文として書くならこんな感じ」というイメージを先に説明します。 open traitとしての説明 次のように、定義済みのError traitを拡張できる仕組みであると説明できます。 ⚠️こ
いかにも既知な気がしますが、ぱっと既存の議論が探せないのでとりあえず書き出してみます。 ※用語は独自のものです。また、証明・定義は厳密に検証しているわけではないので誤りがあるかもしれません。 この定義でカバーできること 先頭 ^ 末尾 $ 肯定先読み (?= ... ) 否定先読み (?! ... ) 肯定後読み (?<= ... ) 否定後読み (?<! ... ) この定義でカバーしないこと 選択の左右の優先度 (r1|r2 vs. r2|r1) 最長優先と最短優先の区別 (r* vs. r*?) 基本の正規表現 基本の正規表現は通常以下のように定義されます。 \Sigma はアルファベットで有限サイズとします。 文字リテラル a (a は文字 a \in \Sigma) 空文字列 \epsilon と連接 R_1R_2 (R_1, R_2 は正規表現) 空集合 \emptyset と
事前条件も事後条件もテストも全部 assert!() でいいの? まあ、いいんじゃないでしょうかという話 Rustでは実行時表明とテスト表明の双方を同じ仕組み (panic機構) を用いて行います。 Rustを書くにあたって、この部分に違和感を覚えた人もいるのではないかと思います (多数派ではないと思いますが)。本稿ではこの違和感について分析し、Rustではそれで問題ないと確認することを目指します。 ※割とフワッとした話に終始します Resultとpanic Rustではエラー処理の方法としてResultとpanicの2種類の方法を提供しています。これは大まかに以下のように使い分けられます。 プログラムが想定しなければいけないエラー (ユーザーが誤った入力を与えた場合や入出力エラーなど) はResultを使う。 プログラムが想定外の状態に陥った場合 (意図しない配列の境界外参照など) はp
本稿では直和による多態性を実現する2つの機能、enumとtraitを拡張性の観点から比較します。 → Expression problem ※enumとtraitを使い分けるにあたってはパフォーマンス要件など他の条件も考慮するべきですが、本記事は多態性の観点のみ説明します。 数式 — enumによる例 本稿では数式をあらわすデータ型を例として扱います。enumではこのように定義されます。 #[derive(Debug)] pub enum Expr { Var(String), Add(Box<Expr>, Box<Expr>), Sub(Box<Expr>, Box<Expr>), Mul(Box<Expr>, Box<Expr>), Div(Box<Expr>, Box<Expr>), } impl Expr { pub fn eval(&self, vars: &HashMap<St
データをシリアライズするには、独自のフォーマットを定めるよりも、基本的な定義済みの構造を組み合わせてフォーマットを作るほうが望ましい場合が多いです。 そのような仕組みとしてJSON, S式, XMLなどが存在しますが、これらは 「基本的な構造」として何を選ぶか、という観点からそれぞれに個性を持っています。 本記事では、具体的な構文のことは基本的に忘れて、各フォーマットが採用するデータモデルの違いに焦点を絞って比較します。 JSON data JSON = Value data Value = -- Compounds Array [Value] | Object (Map String Value) -- Scalars | Null | Boolean Boolean | String String -- UCS-2 | Number IntegerOrFloat -- no NaNs
次のようなパッケージ (クレート) を作ったので解説します。 動機 Rustには所有権があるため、普通のswapパターンが動かない場合があります。これに対応するため、std::mem::swapという関数が用意されています。 let mut x = 'a'; let mut y = 'b'; std::mem::swap(&mut x, &mut y); assert_eq!((x, y), ('b', 'a'));
TypeScriptではデザインパターンとしてtagged unionによる直和がよく使われます。このときパターンマッチに相当する処理はswitchで行われますが、そこで直和に対する分岐が網羅的であることの保証を実行時と型検査時の両方で賢く行う方法がこれまでも模索されてきました。 今回、ヘルパー関数を導入せずにいくつかの問題を同時に解決する賢い方法を思い付いたので共有します。 コード これだけです。 // switch (action.type) { ... default: throw new Error(`Unknown type: ${(action as { type: "__invalid__" }).type}`); // .. } 以下、より詳しく説明します。 問題 TypeScriptではオブジェクトに type プロパティーを用意し、決まった文字列を入れることで直和を実現
※大風呂敷を広げてやる気だけ表明する記事です。まだ中身はない。 はじめに Rustは2009年頃に誕生し、2015年に正式リリースされたプログラミング言語です。その革新性はおよそ以下のように集約されます。 shared XOR mutable とそれに付随する発明 (所有権、ライフタイム) により、ミュータブル参照が正しく扱えるようになった (責任分界点が明確になった) こと。 上記のアイデアにより、高パフォーマンス・低フットプリントが求められるアプリケーションやOS・GC・アロケーターのない環境で動く必要のあるアプリケーションをより安全に書けるようになったこと。 プログラマーが安全に・快適にプログラムを書くための既知の優れた道具 (パッケージマネージャーなど) を計画的に取り入れ、伝統的なプログラミング言語が抱えがちな問題を広範に解決したこと。 これらの革新性から、これまでC/C++が担
Cycloneとは CycloneはRustのリージョン推論の原型のひとつになった実験的なプログラミング言語です。 現在はメンテナンスされていませんが、歴史的な意義があることからCycloneのビルド環境を整備してみました。 (完結するかは未定) ソースの取得 CycloneのWebサイトは生きているので、ソースはCycloneのDownloadページから取得できます。しかしここに不穏な文言があります。 If you use gcc 4, you must get the latest version of Cyclone from SVN (see below). 最新安定版よりも新しい版があること、またgccのバージョンに依存して壊れることが読み取れます。そしてSubversionと書いてあることから嫌な予感がした人もいると思いますが、このリポジトリは既に動いていません。 というわけで
注意事項 おそらくCRuby (いわゆるMRI) 限定です。3.1.2で確認しています。 環境構築が面倒だったため、実際には32bit環境でテストしていません。意図通りに動かなかったらすみません…… 以下解説 doキーワードの多重性 RubyではLRパーサージェネレーターであるBisonを使いながらも、LR(1)とは思えない高度な構文をパースさせるために、字句解析器が非常に複雑な状態管理を行っています。見た目が同じトークンでも、Bisonに渡すときには状態に応じて異なるトークンとして認識させることがあります。 doキーワードもそのような多重性をもつトークンの例で、構文解析器からは以下の5種類のトークンとしてあらわれます: 通常の識別子 foo.do ラムダ式のdo-endブロック -> do end 真のdo-endブロック f g do end 偽のdo-endブロック f do end
for-ofを用いて広義のRAIIを実現する方法を紹介し、JavaScriptでリソース管理を行うパターン3種を比較します。 タイトルはorumin氏の記事のリスペクトです。 RAIIについて RAII (リソース確保は初期化である) はC++におけるリソース管理方式を表す言葉で、デストラクタにより暗黙的にリソース解放処理を行うことでプログラマの注意力に頼らずにリソース管理を行うことができます。 C++やRustのRAIIの特徴は、専用の制御構文を持たずに、変数のスコープに基づいてリソース管理が行われることです。 // C++ RAII // スコープから抜けるときに自動的に解放される std::fstream fs; fs.open("/proc/cpuinfo", std::fstream::in);
Temporal Dead Zone とは ECMAScript 2015で採用された let/const のスコーピングの仕様をTemporal Dead Zoneと呼びます。 const x = "outer"; { const f = () => x; // console.log(f()); // => Error const x = "inner"; console.log(f()); // => "inner" } 4つの方式 Temporal Dead Zone という名前は、ブロックスコープ変数の挙動を決める際の4つの候補の名前に由来しているようです。 これはECMAScript 4が放棄されて間もない2008年10月のes-discussのログ[1]に言及があります。 A1. Lexical dead zone. References textually prior to
ふと思いつきでHTMLパーサーを書いているので、どのようなことを考えながら実装したかの記録を残します。 (1) 字句解析 (2) 構文解析 おことわり 偏ったこだわりや早すぎる最適化もあるかもしれませんが、あくまで趣味で書いているものなのでご理解ください。そういったもののなかにも、他の人の発想のヒントになるものも含まれると考えています。 途中の試行錯誤のサイクルを省いて結論だけを紹介している箇所もあります。ご了承ください。 ソースコードは https://github.com/qnighy/htstream に上げていますが、現在のところ完成度はそれほど高くありません。ドキュメントもほぼありませんがご了承ください。 HTMLについて HTMLの管轄をめぐっては紆余曲折がありましたが、現在はWebブラウザベンダーを中心とする標準化団体であるWHATWGが制定するHTML StandardがH
Learn CSS を眺めているので学習メモ。 規格 関連する規格がAll CSS Specificationsにまとまっているので、適宜参照する。CSSはCSS2を部分的に置き換える形で定義されているが、そのパッチの一覧をまとめたCSS Snapshot 2021等の文書がある。 ボックスとレイアウトについて 001 Box Model, 008 Layout box modelはCSS2§8を置き換える形でCSS-BOX-3で定義されている。 特にborderの扱いはbackgroundの扱いとあわせてCSS-BACKGROUNDS-3で定義されている。これはCSS2§8.5とCSS2§14.2を置き換える。 フラグメンテーションはCSS2§13.3を大幅に拡張し置き換える形でCSS-BREAK-3で定義されている。 Box modelに関するMDNの入門記事もよくまとまっている。 L
(ビルド済みのバイナリや各種ディストリビューション向けのパッケージは現時点では存在しません。貢献を歓迎します!) これは何? 名前の通り、指定した処理を実行しないコマンドです。たとえば、 はfooを作成しません。 dontコマンドは必ずしも「何もしない」とは限りません。特定のパターンに対しては、ユーザーの意図を汲んで特別な処理が行われます。いくつかの例をreadmeに書いてあります。全てのパターンを知りたい人はソースコードを参照してください。 なぜこのタイミングで公開したのか エイプリルフールのネタにしようかなと思ったのですが、他のジョークの話題と食い合うのもな…… と思ったので早めに公開することにしました。 実装上の工夫 基本的には出落ちで終わってしまう話ですが、実装上の工夫がいくつかあるので紹介していきます。 -- の処理 本コマンドはコマンドラインパーサーclapを使っており、オプシ
// CommonJS Modules の場合 const fs = require("fs"); const fs = require("node:fs"); // ES Modules の場合 import fs from "fs"; import fs from "node:fs"; process のように、グローバル変数としても組み込みモジュールとしても提供されているAPIもあります。 global globalThisの別名です。Webブラウザでは window と self がglobalThisの別名として定義されていますが、Node.jsには window や self はなく、かわりに global が定義されています。 Buffer ArrayBuffer, TypedArray (Uint8Arrayなど), DataView はJavaScriptの標準機能です。
次のページ
このページを最初にブックマークしてみませんか?
『Masaki Haraさんの記事一覧』の新着エントリーを見る
j次のブックマーク
k前のブックマーク
lあとで読む
eコメント一覧を開く
oページを開く