ねぎ嫌い

始業前に学んだことを小出しに。最近はHacker Newsの人気記事をまとめてみたり。

Slack Botの実装をbolt x typescript x cloud functionsで実現する

背景

  • Slack上の特定のコマンドやアクションに引っ掛けて、チャンネル作成や通知をしたい
  • 特にチャンネル作成の権限がシステム管理者や特定の役割を持った人に集中している場合に、彼らを通さずに実現したい

TL;DR

  • boltに関しては日本語訳あるので、公式を読む
  • Slackのどの権限が必要なのかはSlack APIを読む
  • Cloud Functions (本文ではFirebase Functionsを利用) の構成はFirebaseの公式を読む

前提条件

  • 自分が自由にできるSlackのworkspaceを用意する (持っていなければ、公式を読もう)
  • 自分が自由にできるGCPのプロジェクトを用意する
  • Node.js (v12以上) がインストールされている (インストールされていなければ 公式の手順に従ってインストールする)

概要

SlackのBoltフレームワークと、Firebase SDKを用いて簡単にSlack Botの実装をする
今回は、Botにメンション付けたらチャットにメッセージを返してくれる簡易的な、何の役にも立たないものを実装する。
やるべきことは大きく分けて3ステップ

  • プロジェクトのセットアップ
  • 実装
  • Slack Botのセットアップ

ステップ

プロジェクトのセットアップ

firebase sdkのインストール

> npm i -g firebase-sdk
> fireabse --version

firebase コマンドが使えていればOK。
続いてFirebase Functionsのプロジェクトを開始する
いくつか質問があるので回答する

> firebase init


# まずはFunctionsを作成したいので、functionsにカーソルを合わせてスペースで選択

? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection)
 ◯ Database: Configure Firebase Realtime Database and deploy rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
❯◯ Functions: Configure and deploy Cloud Functions
 ◯ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules
 ◯ Emulators: Set up local emulators for Firebase features
 ◯ Remote Config: Get, deploy, and rollback configurations for Remote Config

# 利用するGCP (Firebase) のプロジェクトを選択
# 持っていればexisting project, なければ新しく作る

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

? Please select an option: (Use arrow keys)
❯ Use an existing project 
  Create a new project 
  Add Firebase to an existing Google Cloud Platform project 
  Don't set up a default project 

# 型の恩恵を受けたいのでTypescriptを選択する

A functions directory will be created in your project with a Node.js
package pre-configured. Functions can be deployed with firebase deploy.

? What language would you like to use to write Cloud Functions? (Use arrow keys)
❯ JavaScript 
  TypeScript 

# あとは適当で良い。GoogleのESLintは厳し目なので合わない人は選択しないほうが良い

続いて、利用するフレームワーク群のインストールをしておく。
ソースコードはfunctions/以下のディレクトリに集約されるので、どこでnpm スクリプトを動かすかは意識しておく。

> cd functions/
# bolt本体のインストール
> npm i @slack/bolt 

# 型定義をインストール
> npm i -D @types/node 

バックエンドの実装

構成は以下

.
├── jest.config.ts
├── package-lock.json
├── package.json
├── src
│   ├── index.ts
│   ├── slack
│   │   ├── bot.ts
├── tsconfig.json

まずはbot本体の実装から

今回は「メンションが飛んできたら」をトリガーとしたいので、app_mentionのイベントに引っ掛ける形で起動する。
どのイベントに引っ掛けるべきか、は公式 に記載がある。

また、後述の手順で取得する接続情報はFirebaseの環境情報から取得する。
Secrets Manager使ってもいいけど200msくらいオーバーヘッドあるので、、、

import {
  AllMiddlewareArgs,
  App,
  ExpressReceiver,
  SlackEventMiddlewareArgs,
} from "@slack/bolt";

// 接続情報はアプリに対して払いだされるので複数のbotにできるような構造体にしておくと拡張性あって良いかも
// その場合はexpressを利用してルーティングを設定しないとダメ。
type Config = {
  slack: {
    bot: {
      secret: string,
      token: string
    }
  }
}

const config: Config = functions.config() as Config;
const {secret, token} = config.slack.bot;

const expressReceiver = new ExpressReceiver({
  signingSecret: secret,
  endpoints: "/events",
  processBeforeResponse: true,
});

const app = new App({
  receiver: expressReceiver,
  token: token,
});

type AppMentionContext = SlackEventMiddlewareArgs<"app_mention"> &
  AllMiddlewareArgs;

app.event("app_mention", async (ctx: AppMentionContext) => {
  const { event, say } = ctx;
  const message = event.text;
  await say(`message: ${message}`);
});

あとはfunctionsが認識できるようにexportしてあげる

import * as functions from "firebase-functions";
import bot from "./slack/bot";

// `slackbot`がエンドポイントになる。(https://xxxx/slackbot)
exports.slackbot = functions.https.onRequest(bot.app);

接続情報がまだないのでデプロイはしない

Slackの設定

Slack API にアクセスし、Create a custom Appからアプリを登録する。 特に凝ったものを作らないので From scratch を選択する。
アプリの名前を適当に入力し、ワークスペースに自分がどうにでもできるものを指定する。

f:id:anizozina:20210607083036p:plain f:id:anizozina:20210607090034j:plain ※ちょっとエッチな名前つけちゃったので隠した

アプリのセッティングページに遷移するので、Botの実装に利用するSigning Secretの値だけすぐに使えるようにコピーしておく。

f:id:anizozina:20210607083923j:plain

続いて、OAuthトークンの生成を行う。
botが利用する機能に応じてスコープを設定してあげる必要がある。
スコープはScopes and permissionに記載されているので、読んで選択する。

今回はbotのメンションをトリガーとして実行され、チャンネルにチャットを書き込むので以下の権限を設定する

  • chat:write
  • app_mentions:read

f:id:anizozina:20210607090207p:plain

Install Appからインストールを許可するとOAuth Tokenが発行されるので、コピーして持っておく。

ソースコードをデプロイしないと次に進めないので、先にコンソールで

  • Firebase環境情報の設定
  • デプロイ

を実施する。

# Firebase環境情報の設定
# 要: 想定したプロジェクトがデプロイ先に指定されているかの確認

> firebase functions:config:set slack.bot.secret="Signing Secretの値"
> firebase functions:config:set slack.bot.token="OAuth tokenの値"

# Deploy
> firebase deploy --only functions:bot

デプロイ後に生成されるfunctionのエンドポイントをコピーする(https://us-central1-$project.cloudfunctions.net/bot みたいな形式)

Slack側の設定に戻り、Event Subscriptionsの画面でイベントを有効化し、Request URLに↑のURL+ /slack/events を設定する。
うまく設定できていればVerified!のchipが付いてくれる。

f:id:anizozina:20210607085310j:plain

少し下のほうにある Subscribe to bot events の欄で、OAuthトークン発行時に選択したイベントのうちトリガーになるほう(app_mention)を指定する。 f:id:anizozina:20210607085532p:plain

以上。動作させるにはチャットにアプリをインストールする必要があるのでそこだけ最後やってもらえれば。

最後に

細かいところは全然キャッチアップしていないけれど、ふわっとした理解ですぐ作れるのはありがたい。
誤りがあったり補足があったら指摘していただけると助かります mm

S3からShift JISファイルを取り込んで処理をする(Node.js)

ちょっとハマって悔しかったので。

node.jsがShift_JISの変換をうまいことやってくれない。
変換様のパッケージをインストールする。

npm i iconv-lite npm i -D @types/iconv-lite

UTF-8であれば、以下のように楽に取り込める
なお、Typescript

import { S3 } from 'aws-sdk';

export const loadContent = async(bucket: string, path: string): Promise<string> => {
    const s3: S3 = new S3();
    const params: S3.GetObjectRequest = {
      Bucket: bucket,
      Key: path
    };
    const ret: S3.Types.GetObjectOutput = await s3.getObject(params).promise();
    return ret.Body.toString('utf-8');
}

今回はStream APIを使って、バイナリ形式でファイルを取込み、Shift_JISに変換した。

//... 略

    const stream = s3.getObject(params).createReadStream().pipe(iconv.decodeStream('Shift_JIS'));
    let content = '';
    stream.on('data', d => content += d);


    await new Promise(function(resolve, reject) {
      stream.on('end', () => resolve(stream.read()));
      stream.on('error', reject);
    });

    return content;
}

古き良き時代に生きる私はNode v8.10でしかLambdaを動かせないのでAsync Iteratorが使えない。
多分Async Iteratorが使えると、

    for await (const chunk of stream) {
      content += chunk;
    }

で書けそう。

Requested factory com.ctc.wstx.stax.WstxOutputFactory cannot be locatedで怒られる

wsdlファイルからClientを書こうとして、Eclipseでインポートするときに掲題のエラーが発生した。

いろいろいじった結果、wstx-asl-4.0.0.jarをWEB-INF/lib以下に配置し、eclipseを再起動することで解消。

Exception occurred during code generation for WSDL  : javax.xml.stream.FactoryConfigurationError: Requested factory com.ctc.wstx.stax.WstxOutputFactory cannot be located.  Classloader =org.eclipse.osgi.internal.framework.ContextFinder@4d5361a8
    java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.eclipse.jst.ws.axis2.consumption.core.command.Axis2ClientCodegenCommand.execute(Axis2ClientCodegenCommand.java:222)
    at org.eclipse.wst.command.internal.env.core.fragment.CommandFragmentEngine.runCommand(CommandFragmentEngine.java:419)
    at org.eclipse.wst.command.internal.env.core.fragment.CommandFragmentEngine.visitTop(CommandFragmentEngine.java:359)
    at org.eclipse.wst.command.internal.env.core.fragment.CommandFragmentEngine.moveForwardToNextStop(CommandFragmentEngine.java:254)
    at org.eclipse.wst.command.internal.env.ui.widgets.SimpleCommandEngineManager$6.run(SimpleCommandEngineManager.java:294)
    at org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:440)
    at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:356)
    at org.eclipse.jface.wizard.WizardDialog.run(WizardDialog.java:977)
    at org.eclipse.wst.command.internal.env.ui.widgets.SimpleCommandEngineManager.runForwardToNextStop(SimpleCommandEngineManager.java:264)
    at org.eclipse.wst.command.internal.env.ui.widgets.WizardPageManager.runForwardToNextStop(WizardPageManager.java:91)
    at org.eclipse.wst.command.internal.env.ui.widgets.WizardPageManager.performFinish(WizardPageManager.java:262)
    at org.eclipse.wst.command.internal.env.ui.widgets.DynamicWizard.performFinish(DynamicWizard.java:382)
    at org.eclipse.jface.wizard.WizardDialog.finishPressed(WizardDialog.java:775)
    at org.eclipse.jface.wizard.WizardDialog.buttonPressed(WizardDialog.java:414)
    at org.eclipse.jface.dialogs.Dialog.lambda$0(Dialog.java:622)
    at org.eclipse.jface.dialogs.Dialog$$Lambda$55/594043354.accept(Unknown Source)
    at org.eclipse.swt.events.SelectionListener$1.widgetSelected(SelectionListener.java:84)
    at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:252)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
    at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4131)
    at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1055)
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3944)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3547)
    at org.eclipse.jface.window.Window.runEventLoop(Window.java:822)
    at org.eclipse.jface.window.Window.open(Window.java:798)
    at org.eclipse.wst.command.internal.env.ui.widgets.popup.DynamicPopupWizard.run(DynamicPopupWizard.java:130)
    at org.eclipse.ui.internal.PluginAction.runWithEvent(PluginAction.java:250)
    at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:568)
    at org.eclipse.jface.action.ActionContributionItem.lambda$4(ActionContributionItem.java:400)
    at org.eclipse.jface.action.ActionContributionItem$$Lambda$271/560752677.handleEvent(Unknown Source)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:89)
    at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4131)
    at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1055)
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3944)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3547)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$5.run(PartRenderingEngine.java:1173)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
    at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1062)
    at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:156)
    at org.eclipse.ui.internal.Workbench.lambda$3(Workbench.java:636)
    at org.eclipse.ui.internal.Workbench$$Lambda$68/2118050088.run(Unknown Source)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:339)
    at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:563)
    at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:151)
    at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:155)
    at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:203)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:137)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:107)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:255)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:659)
    at org.eclipse.equinox.launcher.Main.basicRun(Main.java:595)
    at org.eclipse.equinox.launcher.Main.run(Main.java:1501)
    Caused by: javax.xml.stream.FactoryConfigurationError: Requested factory com.ctc.wstx.stax.WstxOutputFactory cannot be located.  Classloader =org.eclipse.osgi.internal.framework.ContextFinder@4d5361a8
    at javax.xml.stream.FactoryLocator.loadFactory(FactoryLocator.java:120)
    at javax.xml.stream.FactoryLocator.locate(FactoryLocator.java:109)
    at javax.xml.stream.FactoryLocator.locate(FactoryLocator.java:54)
    at javax.xml.stream.XMLOutputFactory.newInstance(XMLOutputFactory.java:29)
    at org.apache.axis2.util.PolicyUtil.policyComponentToString(PolicyUtil.java:186)
    at org.apache.axis2.wsdl.codegen.emitter.AxisServiceBasedMultiLanguageEmitter.getInputElement(AxisServiceBasedMultiLanguageEmitter.java:2824)
    at org.apache.axis2.wsdl.codegen.emitter.AxisServiceBasedMultiLanguageEmitter.generateMethodElement(AxisServiceBasedMultiLanguageEmitter.java:2358)
    at org.apache.axis2.wsdl.codegen.emitter.AxisServiceBasedMultiLanguageEmitter.loadOperations(AxisServiceBasedMultiLanguageEmitter.java:2242)
    at org.apache.axis2.wsdl.codegen.emitter.AxisServiceBasedMultiLanguageEmitter.createDOMDocumentForCallbackHandler(AxisServiceBasedMultiLanguageEmitter.java:1232)
    at org.apache.axis2.wsdl.codegen.emitter.AxisServiceBasedMultiLanguageEmitter.writeCallBackHandlers(AxisServiceBasedMultiLanguageEmitter.java:1198)
    at org.apache.axis2.wsdl.codegen.emitter.AxisServiceBasedMultiLanguageEmitter.emitStub(AxisServiceBasedMultiLanguageEmitter.java:500)
    at org.apache.axis2.wsdl.codegen.CodeGenerationEngine.generate(CodeGenerationEngine.java:282)
    ... 61 more

2017-10-23 Simple Ways to Be Better at Remembering

原文:www.nytimes.com

何かを思い出すための簡単な方法についてのお話。

平均的な1日では、人は47回もスマートフォンを確認している。
18歳から24歳に限って言えばその倍近くにも登る。
この状態は、何かを覚えておくのには難しい。
スマートフォンは歩行や会話、思考を変え、我々はほとんど追いついていない。

全てはGoogle検索によってほぼ瞬時に手に入れることが出来るのに、
何が役に立たない情報を保持しておく動機になるのだろうか。

このインスタントファクトはどの情報をフィルタリングして保持しておくかの判断を曇らせる。
もはや些細なデータを覚えておくことは重視されず、
大きい認知スペースを押し付けられている。
それではどのように覚えているものを選択しているのだろうか。

2種類の思い出があり、
1つは意識的な経験から作られる「明示的」なものと、
もう1つは過去に経験した恐ろしい体験や未知の出来事といった「暗黙的」なものである。

思い出は時の経過とともに変化する、あてにならないものである。
長期記憶を呼び出すことは、新しいコンテキストを加えた短期記憶として思い出す。
ゆえに、記憶は再構築であり、写真記録ではない。
合理的な目的のために、脳は永遠にその記憶を再録し、
多くのエラーを起こしやすくしてしまう。

多くの人は1つのタスクに継続的・持続的に取り組み、
より多くの成果を上げる可能性があることに気づいていない。
Facebook上で1つの会話から逃げ、別の会話に移ることは爽快かもしれないが、
人はこのプロセスで何を失ったのか気づいていない。
これは、頭の上で美味しいスープを注ぐようなものである。
タスク間の取り組みを最善の方法で共有できていると思っている人は、
実際には多くのものが欠落している。

脳は、処理や扱えることに制限がある。
とはいえ、それらを改善することは出来る。

Repeat After Me

思っているよりも簡単な方法の1つは、タスクを繰り返すこと。
何度も読んだり、単語を繰り返し言葉にしたり。
短期記憶を長期記憶に移動させるのに、最も適した方法である。
これをやるために、1度に1つのタスクに集中することを再教育しなければいけない。
悲しいことに、我々は既に生産的であると確信しているため、この方法を避けてしまう。

何かを学ぶとき、新しいコネクションが脳内で生成される。
学んだことを思い出すために、若いときはこういうことをやっていただろう。
単語を繰り返したり、正しい答えにたどり着くまで考えやアイディアを繰り返したり。

Take Your Time

詰め込み型の勉強のことは忘れる。
大学で上手く行かなかったように、現在も上手く行かない。
間隔反復が最も良い方法だろう。

脳に基本的な事実を詰め込むことは長期記憶において忘れさせてしまう。
知識や技能をなんども復習する時、それはとどまる。
なので、覚えようとしていることを日常生活に盛り込むことができれば、
記憶にとどめておくチャンスは劇的に向上する。

しかし、一度でも知識の復習を止めると、記憶力は大いに抜け落ちてしまう。
研究者はそれを忘却曲線と呼ぶ。

それを乗り越えるために、数日かけて繰り返すことと、効果を自身で確かめること。
ただし、多くのことを同時に繰り返すことはあまり効果が出ないので、
上手く効果の出せる健康的な方法を探すことに気をつける。
これは新しい言語に取り組み効果的な方法である。

Sit Down and Stay Put

記憶とフォーカスは密接に関連している。
フォーカスを改善するための一つの方法として、オフィスを組み替えることを提案している。
シリコンバレーでよくあるオープンオフィスは実際には生産性を大幅に低下させると考えられている。
同僚がドローンを操縦したり、スナックを食べたりしているそばで、
どのようにして一つのタスクに集中することが出来るだろうか。

オープンな職場の復活はこのようなシチュエーションを助けられない。
机もなく、物理的な遮蔽物もなく、プライバシーもなく、ただ遊具だけは多くある職場に言及している。
ヨガ用の部屋、ロッククライミングやガーデンは大きな特典になるが、
多数の挑発のなかで締め切りを守ることは難しくなる。

多数の研究で、先延ばしはストレスになり、完全にフォーカスを殺すことを発見されている。
ウェブサーフィンといった興味をそそるが無用なタスクをやめ、働くのに必要なことだけに取り組む。
そうするとフォーカスは高まり、記憶も改善する。
いわゆる「ライフハック」を追求し、お互いに不正行為を働いていない時、
フォーカスと記憶のパフォーマンスはより良くなる。

Incentivize Moments and Read Cues

精神は常にさまよっている。
学生に対して頻繁にテストを実施することで、
彼らは小テストが実施されることがわかるようになるため、 結果としてフォーカスを促すことになる。
ハーバードの調査ではこの手法は白昼夢を50%削減し、
結果を改善していると報告している。

このトリックはフォーカスにある。
ウェブサーフィンといった幾つかのタスクでは、分割された注意は無害に聞こえるが、
運転しているときはそのタスク以外何もない。
その忘却は人生全体の経過を変える可能性があり、最も深刻な脆弱性は記憶にある。

鍵のようなアイテムに視覚的あるいは言語的なきっかけを採用して、
場所やものを関連付ける方法を提案している。
我々の電子的なデバイスは想起することを助け、
例えば赤子を車のなかに置き去ったことを思い出させてくれる。
馬鹿げた話に聞こえるが、我々が失敗するときの悲劇でもある。

記憶は非常に”きっかけ”に依存している。
何かを参照することをぼんやりとした記憶障害(absent-minded memory failure)と呼ぶ。
多くの人は自分には決して起こらないことだと言うが、おおくのひとは既に起こしてしまっている。
このきっかけがなければ、ほとんどすべてのものを忘れる可能性がある。

ぼんやりとした記憶障害について本当にトリッキーなことは、
反応を起こす必要がある瞬間にきっかけが存在しなければ、
なににでも影響を与えられる可能性がある、ということである。

単純な方法は、リマインダーをセットすることである。
また、次のテクニックを組み合わせることも良い。

  • ポストイットにリマインダーを書いて、机の上に張っておくことで繰り返し目につくことを強制させる
  • 間隔反復の練習を実施する

これらのアプローチを組み合わせて、記憶を構築する。
近代の生活に保証はほとんどないが、これらのヒントを使うことできっと改善することができる。

多くの人々は注意散漫を扱うことが出来る、と自信過剰になっている。
1度に2つのことを行うことは常に影響している。
お金を払うときのように、注意を払うときは自身のおかれた状況を認識し、理解すること。 状況によっては問題にならないかもしれないが、その他の状況では全てを変えてしまうかもしれない。

2017-12-05 Introduction to logic programming with Prolog

原文:Introduction to logic programming with Prolog

Prologと言う論理型言語についてのお話。

筆者は大学時代に言語とパラダイムについて研究をしていて、
その中で多くの言語に触れ、良し悪しを把握してきた。
そのなかで、論理型言語であるPrologを計量言語学とAIに多く用いていた。

この記事ではPrologの軽い紹介と不可欠な機能について述べられている。

そもそも、なぜPrologやその他の言語を学ぶモチベーションがあるのかというと、
「達人プログラマー」でも以下に述べられているように、多くの言語を学ぶことで多くの解決策を仕入れることが出来るからである。

毎年少なくとも1つの言語を学ぶ。
異なる言語は同じ問題を異なる方法で解決している。
異なるアプローチを学ぶことは、考え方を広げ、同じ轍を踏まないように積み上げられる。
付け加えると、インターネット上で自由に使える豊富なソフトウェアのおかげで、現在では多くの言語を学ぶことははるかに簡単になっている。

例えば数独パズルをJavaやC、Prologで解くなど。

まず、論理型言語が何かについて述べる。

どのように完了するか、ではなく何がしたいかを述べる

論理型言語は数学の論理を基礎に持つプログラミングのパラダイムの1つである。
JavaやCと比べると、論理型言語で書かれたプログラムは連続した説明書のように構成されているわけではなく、公理の集合やオブジェクト間の関係の規則で成り立っている。
同様に命令型ではなく、宣言型のアプローチを採用している。

Prologはホーン節(第一階述語論理の部分集合)を基にしていて、多くの論理型言語と同様である。
Prologは1972年に初めて登場したが、現在では定理証明やエキスパートシステム自然言語処理あるいはAIにの領域で非常によく使われている。
また、Erlangの開発にも大きく影響を与えている。

他に、以下についても触れている

  • 言語構造
  • 基本的な「事実」とクエリ
  • ユニフィケーション
  • リスト表記
  • Prolog シェル
  • 回帰処理
  • 四色定理