Scala で for 式の中括弧{} 内で <- expression が受け取れる型について

どういう経緯だったか忘れてしまったのだけれど

Haskell の do 記法は Monad 型クラスの >>= (bind?) 等に変換される糖衣構文で、do 記法で使えるのは Monad 型クラスのインスタンスを持つ型だけだけど、Scala の for 式はどういう形で受け取る型を決めているんだろう?

という話になり、「TraversableflatMap とか定義されているから Traversable が受け入れられるのでは?」と素朴に思ったのですが、Option とか Future とか明らかに違うやつがいくらでもいるので調べました。

※さらにいうと、Scala 仕様書の for のところ見ても糖衣構文だよーとかどう分解するかということまでは書いてあるものの受け入れられる型みたいな話はなかったり、"Scala for 受け入れられる型" みたいなググり方をしてもなかなかたどり着けなかったので、私みたいな検索弱者用に書く

公式ドキュメントにめっちゃ書いてあった

Scala’s “for comprehensions” are syntactic sugar for composition of multiple operations with foreach, map, flatMap, filter or withFilter. Scala actually translates a for-expression into calls to those methods, so any class providing them, or a subset of them, can be used with for comprehensions.

ただし、How does yield work? という yield に特化したタイトルで、 Scala FAQs って謎階層にいたので、みつからないよ...って思った。(Scala の Gitter チャンネルで Tsuyoshi Yoshizawa さんに教えていただいた)

ためした

object Main extends App {
  val forPo = new Forable("Po")
  
  val po = for {
    u <- forPo
  } yield u
  
  println(po)
  
  // Kaboom! Cannot compile - lack of flatMap
  // val popo = for {
  //   u <- forPo
  //   v <- forPo
  // } yield u + v
  
  val multiForPo = new MultiForable("Po")
  val multiForPoPo = new MultiForable("PoPo")
  
  val popopo = for {
    po <- multiForPo
    popo <- multiForPoPo
  } yield po + popo
  
  println(popopo)
  
  // Kaboom! Cannot compile - lack of foreach
  // for {
  //   po <- multiForPo
  //   popo <- multiForPoPo
  // } println(po + popo)
  
  val sideEffectPo = new SideEffectForable("Po")
  val sideEffectPoPo = new SideEffectForable("PoPo")
  
  for {
    po <- sideEffectPo
    popo <- sideEffectPoPo
  } println(po + popo)
  
  // Kaboom! Cannot compile - lack of filter
  // val popo = for {
  //   popo <- multiForPoPo if multiForPoPo.x == "Po"
  // } yield popo
  
  val filterPoPo = new FilterForable("PoPo")
  
  val popo = for {
    popo <- filterPoPo if filterPoPo.x == "PoPo"
  } yield popo
  
  println(popo)
}

class Forable[A](val x: A) {
  def map[B](f: A => B): Forable[B] = new Forable(f(x))
  override def toString(): String = x.toString
}

class MultiForable[A](x: A) extends Forable(x) {
  def flatMap[B](f: A => Forable[B]): Forable[B] = f(x)
}

class SideEffectForable[A](val x: A) {
  def foreach(f: A => Unit): Unit = f(x)
}

class FilterForable[A](x: A) extends MultiForable(x) {
  def withFilter(expr: A => Boolean): FilterForable[A] = this // easy implementation
}

ダラダラと書いたけど、つまるところ map とかのシグネチャを満たしてれば普通に for comprehension<- の右側に使えることがわかった。

Scala の for 式は実は単に flatMap 等のへの糖衣構文だったり明らかに Haskelldo 記法 をパクった に近いにも関わらず、Haskelldo 記法が Monad 型クラスを要求するのとは結構毛色が違う感じがする。

受け入れられるシグネチャについても結構ゆるくて

class Forable[A](val x: A) {
  def map(f: A => A): MultiForable[A] = new MultiForable(f(x))
}

とか、かなり原型をとどめていなくても行けたので、シグネチャというより、本当にメソッド名さえあっていれば良くて、脱糖されてから型検査が通ればオッケー(実際脱糖は型検査以前に行われるらしい)、という感じのようだ。

結構ふわっとした仕様でちょっと意外だった。

おしまい。

エスペラント入門 #2 ~ または、ことのはアムリラート

本日の学習

ことのは 意味 備考
ボーナン マテーノン あいさつ? ボーネ や ボネーゲ がポジティブなようなので good morning 的な?
ファルタス キーエル ヴィ ファルタス? という形が何回か見かける
プレータ プレート = Dish? マテンマンヂョ エスタス プレータ = 朝ごはんです?
ヴィズィタント visitor
グーテン good? グーテン アペティート で食事の挨拶?おなかへった?
アペティート appetite? 同上
ヴェーレ チュ ヴェーレ = ホント?
ボーネ いいね? ボネーゲ が前回も褒められていたのでポジティブな単語らしい
チェース 待って? ヴィ ネ ベゾーナス スクリービ ミーアン ノーモン! と続いたが…
ゾル 悪い? ゾルグ!という形で「悪くないよ!」的文脈で
エラーラス 間違い? error だろうなぁ
カゥズィス Cause? オカーズィス(前回)同様 cause 系の文脈で出現してる気がする
コムプレーナス 前回のものと同じ? ミ コムプレーナス = 大丈夫?安心して?
ヴェンデーヨ
レゴーマ 野菜
フィシュ
ヴィアンド
パノ パン
クク ケーキ
リズブル おにぎり?
リブロ
the? 仏語?
カーフォ コーヒー
ペフェクテ perfect?
プルス plus
ミーアン my?
ノーモン 名前? name やろなぁ

単語レベルではわからんやつ

徐々に単語レベルではなくガンガン文章としてエスペラントが出てきたので、もはや単語での推測は困難になってきた。ので、文章単位で意味を予測していくことにした。

  • キーウン ヴィ プレフェーラス、チュ ポムスーコン アゥ オランデジュスーコン.....

    • キー* は疑問詞
    • プレフェーラス は謎だけど prefer か?
      • だとすると キーウン は Which かも。Which do you prefer?
    • ポムスーコン と オランデジュスーコン が語形から並置されているとすると アゥ が or か
    • 飲み物を運ぶシーンだけど、オレンジジュースってのは安直すぎるか…?
  • ニ マルシュ マーノン エン マーノ

    • 上のと同じで エン が and?
    • マルシュ が verb?
      • 近い発音だと仏語の marché = 市場だし後に商店街に行くので正しそうだが、品詞的には marche = 散歩 (march と同語源?)のほうが正しいかもしれない
  • レゴーモ、フィーショ、ヴィアンド

    • レゴム-ヴェンデーヨ、フィシュ-ヴェンデーヨ、ヴィアンド-ヴェンデーヨ
      • 舞台は商店街
      • ヴェンデーヨ = 店?
      • レゴーマ サンドヴィーチョ が野菜サンドだったっぽいので、八百屋、魚屋、肉屋、と来そう
    • どうも活用?して語尾が 〇〇eーyo みたいな感じになると〇〇屋になる?
  • チュ ヴィ ヴォーラス マンヂ パーノン?

    • Do you おなかへり? Want パン?
      • 形容詞に対しても チュ (Do っぽいと予測しているやつ) つかう?
  • ラ "ヴォルト-ラディーコ" エスタス "リブル"、ド "リブル プルス 『エーヨ』" ファリーヂャス "リブレーヨ"

    • ヴォルト-ラディーコ = 無活用形?
      • 無活用形がリブル、で、リブルとエーヨを足して finally リブレーヨ
  • ヴェーヌ アル ミ

    • 私についてきて?
  • ヴィ ネ ベゾーナス スクリービ ミーアン ノーモン!

    • ヴィーアン が あなたの と予測される文脈があったので、ミーアン は 私の かも
    • だとすると ノーモン は話の流れ的に 名前
    • ベゾーナス か スクリービ が書くという動詞のはずだけど不明

感想

f:id:biacco42:20171011011333p:plain

ルカちゃんが非常にかわいくなってきたけれど、彼女は日本語がほぼできないので、感情的な表現・流暢な表現となるとエスペラントなので、はやくルカちゃんと自由にコミュニケーション取れるようになってルカちゃんと仲良くなりたい...

だいぶ一筋縄には行かなくなってきた。単語や文章の意味を真面目に考えてメモしながらすすめるとなかなか進まない。けど、Ruka ちゃんがめっちゃ優しくがんばって教えてくれるのでがんばる。

エスペラント入門 #1 ~ または、ことのはアムリラート

ことのはアムリラートをはじめた

ことのはアムリラートという純百合 ADV を標榜するゲームがあります。当然『純百合』の時点で耳目を集めることは必定であるのですけれども、このゲーム、なんといっても異世界転生 ※ただし異世界の言葉は理解できない という、「なんで異世界転生したのにいきなり言葉が通じるんだよ」という消費者の声に真摯に応えた作品で、以下のような画面が展開される大変エモーショナルな作品として注目を浴びていました。

f:id:biacco42:20171007225329p:plain

典型的な (異世界での) 少女同士の出会いの一幕

この異世界言語は全くのオリジナルではなく、基本的にはエスペラントだそうです(厳密にはエスペラントと異なる部分もあるらしい)。

言語監修

藤巻謙一 『著書「はじめてのエスペラント」他』

協力(言語監修)

一般財団法人日本エスペラント協会

というわけで、ことのはアムリラートでエスペラント入門、です。

本日の学習

【注意】 ここから先はネタバレを含みます!

といってもエスペラントの話なのでネタバレもへったくれもないとは思いますが、ストーリーの進行と一致しているので念のため。

意味に?がついているものは完全な予測なので全く外れている可能性もあります。

ことのは 意味 備考
エス Yes
No
ヴィ あなた
私達
パルドーヌ ごめんなさい 英語の Pardon と同語源?
ダンコン ありがとう 独語の Danke と同語源?
キーオ 疑問詞? 文法的には文頭について疑問文をつくっている?
キーエ Where?
ヴァルマ あたたかい
ヴァルメーガ あつい
アクヴォ "ユ" => 湯 が ヴァルマ アクヴォ
オムレート オムレツ そのまま。朝ごはんだった
タグメーゾ 独語の Tag と同語源?
ィエ ラ ウヌーア 1 時
レゴーマ サンドヴィーチョ 野菜? サンドウィッチ
エクイーリ 出かける
アクツィデント 事故・Accident
ウーヌ 1
ドゥ 2
トリ 3
クヴァル 4
クヴィン 5
ヂス ラ じゃあね?
何らかの冠詞 or 前置詞?
レルネーヨ 学校?
ヴィーアン Your? You の所有格か?
セド But?
ホーロ When?時刻?
レヴェーニス 探す?
ボネーゲ すごい?
コンプレーナス 完璧? Complete?
ティーミス 心配する?
オカーズィス 起こる? Occur から根拠レス予測
イーア Some?
アル 前置詞?
エスタス やたら出て来るけれど不明。疑問文に出現しやすい気がする...
エスティス 活用形?ネ と同時に出現。Be っぽい運用な気がする...
チュ 単純疑問で使われている気がする。Do?

セリフはエスペラントではなくカタカナ表記なので、エスペラントについては完全な文盲になりそう。綴りがわからないの結構つらい....

f:id:biacco42:20171007232130p:plain

これは...わからないなぁ....

iOSDC 2017 でさらっと出てきた Phantom Type さらっとやった話

これはなに

先日開催された iOSDC 2017 に参加しました記事です。

Ray Fix 氏の 視覚化とSwiftのタイプについて という発表で

protocol CoordinateSpace {}

enum ModelSpace:   CoordinateSpace {}
enum UserSpace:    CoordinateSpace {}
enum DeviceSpace:  CoordinateSpace {}

struct CGPointT<Space: CoordinateSpace>: Point2DProtocol {
  var xy: CGPoint
  // 実装
}

struct CGAffineTransformT<From: CoordinateSpace, To: CoordinateSpace> {
  var matrix: CGAffineTransform
  public func inverted() -> CGAffineTransforT<To, From> {
    return CGAffineTransformT<To, From>(matrix.inverted())
  }
}

public func * <From, To, I>(from: CGAffineTransformT<From, I>,
                            to: CGAffineTransformT<I, To>) -> CGAffineTransformT<From, To> {
  return CGAffineTransformT<From, To>(from.matrix.concatenating(to.matrix))
}

みたいなサンプルコードが出てきて

let modelToUser: CGAffineTransformT<ModleSpace, UserSpace> = ...
let userToDevice: CGAffineTransformT<UserSpace, DeviceSpace> = ...
let modelToDevice = modelToUser * userToDevice // CGAffineTransformT<ModelSpace, DeviceSpace>

これ型パラメータ実装ないし、実質処理はなにもしてなくない?

というのが、幽霊型 (Phantom Type) です。

Phantom Type

上のコードがほぼ全てですが、型の状態 (モデル空間にある頂点なのかユーザー空間にある頂点なのか) を型パラメータとして表現することで、実行時ではなくコンパイル時に状態を検査することができます。が、この型パラメータは実装では利用されておらずコンパイルしたら消えてしまうので、幽霊型 (Phantom Type) というわけです。洒落てますね。

状態に依存したコード

struct StateDependent {
    var ready: Bool = false

    func doPo() {
        precondition(ready, "Not ready!")
        print("Po")
    }
}

let stateDependent = StateDependent()
hoge.doPo() // Boom! Exception in runtime!

Phantom Type で状態を型で表現したコード

protocol State {}
enum Ready: State {}
enum NotReady: State {}

struct PhantomTypeSafe<S: State> {
    static func create() -> PhantomTypeSafe<NotReady> {
        return PhantomTypeSafe<NotReady>()
    }

    func toReady() -> PhantomTypeSafe<Ready> {
        return PhantomTypeSafe<Ready>()
    }
}

extension PhantomTypeSafe where S == Ready {
    func doPo() {
        print("Po")
    }
}

let phantomTypeSafe = PhantomTypeSafe<NotReady>.create()
//phantomTypeSafe.doPo() // Cannot compile
phantomTypeSafe.toReady().doPo()

Phantom Type = コンパイル時に型検査するためのタグのようなもの

上記サンプルだと、状態を表す変数 var ready をフィールドに持った型では、実行時までエラーが検出できませんが、Phantom Type を用いた型では extensionwhere 句による制約を設けることで、ある 型の状態 でないと呼べない関数を表現することができており、コンパイル時に静的にエラーにできます。

Ray Fix 氏のサンプルだと、CGAffineTransformTModelSpace でも UserSpace でも型としての性質や実装は変わらないので、下手に型を増やさずに Phantom Type で状態を表現して、変換同士の接続に破綻がないかを型で検証できています。

繰り返しになりますが、型パラメータ自体は利用されておらず、あくまで状態を表現する変数のようなものとして扱われているところがおもしろいところです。

Phantom Type で型の条件付け

これだけだとアレなのでもうひとネタ。

ジェネリクスのある言語ならこの Phantom Type のテクニックは使えると思うのですが、関数の引数に受け取る型に制約をつけるといえば他にもあった気がしてきました。型クラスですね

というわけで Scala で Phantom Type と impicit を組み合わせて、受け入れる型に制約をつけてみます。

sealed abstract class =:=[From, To] extends (From => To) with Serializable
private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }

object =:= {
  implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]
}

def doPoWhenEqualType[A, B](implicit et: A =:= B) = "Po"

val po = hoge[Int, Int] // Po
// val po = hoge[Int, String] // compile error

上記の =:= の定義は実際に Predef という、Scala が標準で読み込む定義に含まれています。

Swift でもやってみました。

class TypeTag<T, U> {}

func doPoWhenEqualType<T>(_ tag: TypeTag<T, T>) -> String {
    return "Po"
}

//let po = doPoWhenEqualType(tag: TypeTag<Int, String>()) // Cannot compile
let po = doPoWhenEqualType(TypeTag<Int, Int>())

なんかもっとうまく書けそうな気がします…

というわけで、Phantom Type をさらっとだけ見てみました。

おしまい。

HyperX cloud 2 を買ってはいけない

先日購入した HyperX cloud 2 という バーチャル7.1サラウンドサウンド を謳う 誇大広告商品 があまりにも悪質なので記事を書きます。

結論: 消費者を騙すつもりの製品を売る HyperX ブランド、特に HyperX cloud 2 については完全に誇大広告・事実誤認を意図した商品であるため絶対に購入するべきではない

ゲーミングサラウンドサウンドヘッドセット

この記事を読む時点でゲーミング、とくに 7.1 ch のバーチャルサラウンドサウンドヘッドセットを探している人だと思うので詳細は省きますが、5.1 ch や 7.1 ch 等のマルチチャンネルオーディオは FPS 等のゲームで音の到来方向がわかりやすくて有利なんて話もあり、昨今の PC ゲーミング市場の盛り上がりもあっていろいろと製品が投入されています。

マルチチャンネルオーディオとサラウンドサウンド

そもそも 7.1 ch のサラウンドサウンドオーディオは、7 個のスピーカーを物理的に利用者の周囲に配置して、ゲーム内で後ろから鳴る音はちゃんと後ろのスピーカーから鳴る (から敵の位置とかがわかりやすい) というやつです。もちろんこの 7 個のスピーカーはそれぞれの方向の音源を代表しており、ほぼそれっぽくなるようにそれぞれのチャンネル (7.1 “ch” のチャンネルはこれのこと。スピーカーの数と思ってもらってとりあえずは問題ないです) ごとに計算して鳴らしています。

もちろんこのそれぞれのチャンネルのオーディオを計算するには ゲーム内の情報が必要です。どこで音が鳴っているかを知っているのはゲームだけであり、そのゲーム内の幾何情報をもとにどのスピーカーでどんな音を流せばいいかを計算することで、はじめてサラウンドサウンドが実現できます。当然 2 ch ステレオで生成されたオーディオから 7.1 ch の情報を再現することはできません

ヴァーチャルサラウンドサウンド(本物)

まともなヴァーチャルサラウンドサウンドヘッドセットは、入力ソース自体が当然マルチチャンネルオーディオとなっており、事前にゲーム側でゲーム内情報をもとに計算された 7.1 ch オーディオを、ある空間で (例えば部屋) などで 7 個のスピーカーを置いた場合に耳 = 2 ch の入力で聞くとどのように聞こえるかを計算して、2 ch のヘッドホンで聞いても 7 個のスピーカーから音がなっているように感じることができる、という技術です。(詳しくは伝達関数・とくに頭部伝達関数等で調べてください)

7.1 ch の信号から、それをあたかも部屋で聞いたかのように 2 ch の信号に変換することから各社 バーチャル サラウンドサウンドと呼称しています。実際には 7 個のスピーカーはないのに 7 個のスピーカーから音がなっているように感じるからヴァーチャル(実質的) 7.1 ch なわけです。重要なのは 7.1 ch の情報からシミュレートして 2 ch の情報に情報量を落とすことはできるが逆はできない ということです。

HyperX cloud 2 の事実誤認誘導 “ヴァーチャルサラウンドサウンド”

HyperX cloud 2 の web ページ等での広報文言を引用します。

バーチャル7.1サラウンドサウンド

バーチャル7.1サラウンドサウンドによりオーディオの精度がさらに上がり、ゲーミングにおいて優位性を高めることができます

Cloudゲーミングヘッドセット - Cloud Core、Cloud、Cloud II、CloudX | HyperX - 主な特徴

この文言を見て、みなさん HyperX cloud 2 は 7.1 ch のサラウンドサウンドの 2 ch への変換に対応していると感じたことと思います。しかしこれは上述の 7.1 ch ヴァーチャルサラウンドサウンドとは 全く異なる技術 であり、ユーザーが期待するような空間情報を持ったサラウンドサウンドは 一切実現できません。HyperX cloud 2 はそもそも 2 ch オーディオしか受け付けることができません。そして一度 2 ch に生成された音声信号から 7.1 ch 信号を復元することはできません

前述の通り、まともな 7.1 ch ヴァーチャルサラウンドサウンドは、もともとゲーム内の幾何情報を活用して作成された 7.1 ch の信号をもとに 7 個のスピーカーが鳴っているように感じさせる 2 ch 音源を生成する技術でした。

しかし、HyperX cloud 2 はもともとデバイスの仕様上 2 ch のオーディオしか受け付けることができないため、そもそも正確な位置情報を持つサラウンドサウンドを再現することは不可能です。では、HyperX cloud 2 の言う “バーチャル7.1サラウンドサウンド” とは何なのでしょうか?これが大変お粗末で、単に 2 ch の信号にリバーブ等の適当にぼんやり 7.1 ch 風に聞こえるエフェクトを加える機能 です。はっきり言っておもちゃです。FPS 等のゲームをする際には むしろ音の到来方向がわからなくなる最悪な機能 です。これが ゲーミング ヘッドセットだというのだから笑わせてくれます。

HyperX cloud 2 の悪質な点

これだけだったら無知なメーカーの広報が何かを間違ってマーケティングをしてしまっただけかもしれませんが、残念ながら HyperX は 明らかに意図的にこの不誠実な表現を用いており非常に悪質です

以下、HyperX およびそのブランドを展開している Kingston が “バーチャル7.1サラウンドサウンド” がヴァーチャル 7.1 ch サラウンドサウンドではないということを自覚した上で意図的に消費者を騙そうとしている証拠を挙げます。

もう気づいた方もいるかもしれませんが、HyperX cloud 2 の問題の機能名は “バーチャル7.1サラウンドサウンド” です。これは私の誤植ではありません。"バーチャル7.1 ch サラウンドサウンド" ではないのです。

HyperX cloud の紹介サイトでは巧みにこの 7.17.1ch という表現が 使い分けられています。そう、この表現は脱字などではなく、明らかに意図的に ch という表記が落とされています。

以下、HyperX cloud の紹介ページの冒頭部分の一部を引用します。

多彩なCloudシリーズのヘッドセットファミリーは、あらゆるゲーマーのニーズ、プレー環境、プレースタイル、パーソナルスタイルに対応するように設計されています。PCゲームでも据え置きゲーム機でも、7.1chバーチャルサラウンド対応ヘッドセットをお探しならCloudシリーズからご検討ください。

ここで言う “7.1chバーチャルサラウンド” というのはユーザーが探しているものであり、HyperX cloud 2 の特徴ではないため嘘はありません。私だって 7.1 ch ヴァーチャルサラウンドサウンドヘッドセットを探していました。HyperX はここではわざわざ ch の表記を用いています。

続いて、HyperX cloud 2 の問題の機能について紹介した文言を改めて引用します。

バーチャル7.1サラウンドサウンド

バーチャル7.1サラウンドサウンドによりオーディオの精度がさらに上がり、ゲーミングにおいて優位性を高めることができます。

ここではわざわざ ch を外し、"バーチャル7.1サラウンドサウンド" と表記しています。前述の通り、HyperX cloud 2 は 2 ch オーディオしか受け付けないため、7.1 ch と書けば なにが 7.1 ch なのかすぐさま問い詰められてしまうでしょう。そこで HyperX はここの ch を削除することでなんの意味だかわからない 7.1 という数字だけを付け加えるという作戦に出ます。7.1 は 7.1 であって、別に 7.1 ch とは言っていない、ということでしょう。これ以降も、製品の仕様に関わる部分については “バーチャル7.1サラウンドサウンド” という文言で逃げ切っています。

さらに、HyperX cloud 2 のこの事実誤認狙いの 7.1 表現を知った上で、上記の紹介文言を読むとその狡猾さがさらに浮き彫りになります。

紹介文言には

バーチャル7.1サラウンドサウンドによりオーディオの精度がさらに上がり、ゲーミングにおいて優位性を高めることができます。

とあり、サラウンドサウンドでゲームに優位になる = 音の定位 (音の到来方向) がわかりやすく優位になるという誤認を誘起するような内容になっていますが、

  • 定位や音の到来方向といった具体的表現を避ける
  • オーディオの “精度” という何を言っているかわからない表現を使う

等、巧妙に誇大広告と取られかねない表現を回避しています。

これらを見るに、HyperX が HyperX cloud 2 について、誠実に製品広報を行う気がないことは言い逃れようのないものだと思われます。

オーディオは「わかりづらい」

私も上記の HyperX の狡猾な戦略にハマり HyperX cloud 2 を購入してしまったのですが、それをはじめて使ったときの違和感と不信感というのはなんとも表現しがたいものがあります。

オーディオ沼や、もう少し強い言い方をすればオーディオオカルトという言葉を聞いたことがあるかと思います。デジタル信号を伝送するケーブルを変えると音質が変わるとか、発電所によって音色が変わるとか、高音質の SD カードとかいうアレです。大概のリテラシのある人は、こういった商品について「何を馬鹿な」と一蹴できることと思いますが、微妙なのはオーディオの良し悪しというのは実際非常にわかりづらいのです。だからこそこんなインチキ商売がなりたっているのです。こんなに高価だし良いものらしいが、違いがわからないのは自分の耳が悪いだけかもしれない。実際に良くなっているに違いない。という消費者の不安等につけ込む悪質なビジネスを許してしまったオーディオ業界の傲慢です。

HyperX cloud 2 を買ってきて “バーチャル7.1サラウンドサウンド” を試したときの私はまさにこの状態でした。体感としては明らかに定位が悪くなっているのです。しかしまさか…という思いでしばらく試行錯誤をすすめるものの、結論は “バーチャル7.1サラウンドサウンド” を切ったほうが音の定位がはっきりするということでした。この結論、自分の耳の正しさを信じるまでには非常に不安でストレスがかかりました。

その後、7.1 ch 音源の再生を試してみるとやはりフロント LR チャネルしか鳴らないなど疑念は確信に変わり、Kingston に対する “バーチャル7.1サラウンドサウンド” は単なる WOW エフェクトではないか? という問い合わせをした記事も発見し、HyperX cloud 2 が事実誤認を誘導する悪質な商品であることがわかりました

HyperX cloud 2 は決して買ってはいけない

ここまで説明してきたとおり、HyperX cloud 2 は「こう言っとけば勝手に騙されるだろう」という消費者を馬鹿にした態度で作られた製品であり、決して買うべきではありません。特に安価な商品でもないため、他のまともなメーカーのサラウンドサウンドヘッドセットを購入することをおすすめします。

HyperX は Kingston が展開するゲーミングブランドとのことですが、このような詐欺まがいの商品展開をするメーカーの品など不安で絶対に買えません。HyperX シリーズはもちろん、Kingston 製品を買うことも二度とないでしょう。

こういった不誠実なメーカーが淘汰されることを願ってやみません。

おまけ HyperX のお粗末な(しかしやはり悪質な)マーケティング

HyperX cloud のページには 賞歴という項目があります。普通の感性を持っていたら、まぁそれなりに権威のある(もしくはせめて金で買い付けたものでも)賞が並んでいるのかと思ったらそうではありません。なんと 商品レビューブログ 記事へのリンクなんです。しかもサンプルを渡して書かせたようなやつで、内容は推して知るべしというか素人ユーチューバーのそれを思い浮かべてください… 賞歴とは…

どうせ賞歴とかこんなところまともに読むやついないやろw とりあえずたくさんあったらすごそうじゃんw みたいな感じだったんですかね。徹底的に消費者を馬鹿にするそのスタイルにもはや清々しさすら感じます。

しかし最悪なのが、レビューしているのが本当にこういったことに疎い素人なのか、HyperX の狡猾な戦略にまんまとハマって 7.1 ch ヴァーチャルサラウンド みたいにブログ記事で書いちゃってるんですよね。地獄かよ…

というわけで貴重な時間を無駄にしました。おしまい。

20 分で IntelliJ IDEA と Gradle で Kotlin 開発環境を作って fatJar ビルドしてコマンドラインツールしたり Kotlin Playground したりする話

Kotlin 入門の入門

Google IO で Android の開発言語として Kotlin が正式に採用されたりなど、最近またにわかに注目を浴びている Kotlin ですが

  • 別に Android 開発がしたいわけじゃない
  • Better Java として Kotlin は触っておきたいけど、Android Studio 入れたり実機で動作させるのは面倒
  • Kotlin 自体を学ぶのに Android の周辺知識が必要なのはちょっと…

のように感じている人もわりといるのではないかと思います。

そういう人Scala をやるといいですよのための、Kotlin 入門のための入門記事です。

この記事では

  • Android に依存せず Kotlin が気軽に書ける IDE とビルドシステムの導入
  • Kotlin で非 Android アプリケーション開発・ビルド・fatJar パッキング
  • Swift PlayGround 的な Kotlin PlayGround 環境の構築

20 分でできる ことを目的とします。

扱わないこと

  • Kotlin 自体の言語仕様
  • Gradle ビルドシステムの詳細

IntelliJ IDEA のインストー

www.jetbrains.com

Kotlin は IntelliJ IDEA を開発している Jetbrains 社が開発している言語なので、IntelliJ でのサポートはばっちりというか、概ね IntelliJ をインストールしたら Kotlin の開発環境は整います。コンパイラ・Gradle こみなので他のダウンロードは不要です。

起動したら初期設定を適当に済ませてください。初期設定時におすすめプラグインとして Scala が出てくるので、インストールしておくと良いでしょう。

ここまで 5 分ぐらい

Gradle プロジェクトの作成と Gradle ビルド設定

プロジェクトの生成

IntelliJ を起動・初期設定したらさっそく Kotlin のプロジェクトを作ります。新しいプロジェクトの作成を選択して、プロジェクトのタイプで Gradle を選択します。扱うフレームワークとして Kotlin にチェックをいれる。

f:id:biacco42:20170524120920p:plain

Gradle 用に GroupId と ArtifactId を設定します。 GroupId は Java の package と同様にトップレベルドメインから。ArtifactId はアプリ名。

f:id:biacco42:20170524121526p:plain

プロジェクトの生成オプション。個人的には全部チェックつけるのがおすすめ。特に Create directries for empty content roots automatically は Gradle のビルド規約に則ったディレクトリを自動生成してくれるので、Gradle に慣れてない人はとりあえずチェックをつけておいたほうがよいです。

f:id:biacco42:20170524122911p:plain

最後に保存ディレクトリを決めて Finish。

エントリポイントの作成

Gradle の規約でソースコードsrc/main/言語/package階層 に配置します。まずは src/main/kotlin/ に適当なパッケージ(jp.co.biacco42.app 等)を追加し、そこに Kotlin ファイル Main.kt を追加します。

Main.kt に以下の通り記述します。

fun main(args: Array<String>) {
    println("Hello Kotlin")
}

Kotlin ではトップレベル関数が定義でき main(args: Array<String>) がいわゆる Java の Main クラスの public static void main(String[] args) エントリポイントに相当します。

Gradle のビルドスクリプト設定

上記プログラムの fatJar が吐けるように build.gradle の最後に以下を追記します。

task fatJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'jp.co.biacco42.app.tanoshiilife.MainKt'
    }

    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

manifest の attributes では、jar をそのまま起動できるように、エントリポイントを持つクラスを Main-Class として指定しています。

先程、Kotlin はトップレベル関数が定義できると書きましたが、Kotlin はあくまで JVM 向けのバイナリを生成するので、これらのトップレベル関数もコンパイルした時点でなんがしらのクラスに所属することになります。それが manifest に記載した MainKt です。Kotlin でトップレベル関数を定義すると ファイル名Kt というクラスが定義されて、そのクラスのスタティックメソッドとしてコンパイルされます。

ここまで 10 分ぐらい

ビルドとテスト、Kotlin Playground

早速上記のプロジェクトをビルド・実行してみます。

ビルド

IntelliJ の右上にある Gradle タブをクリックして開きます。

f:id:biacco42:20170524142816p:plain

先程追加した Gradle task fatJar は、Tasks -> other にあるので、これをダブルクリックすると Gradel task を実行できます。

f:id:biacco42:20170524162520p:plain

ビルド完了したら、パッキング済みの fatJar が <ProjectDirectry>/build/libs にできているはずです。実行してみましょう。

$ java -jar <ProjectDirectly>/build/libs/tanoshii-life-1.0.0-SNAPSHOT.jar

Hello Kotlin と表示されれば OK です。これで実行可能なバイナリを Kotlin でつくることができました。

ただ、毎回ビルドして実行はめんどくさいので、Gradle の test task を使って PlayGround 的なものを作ります。

ここまで 15 分ぐらい

Kotlin PlayGround をつくる

src/test/kotlin/PlayGround.kt というファイルを追加して、以下をコピペします。

import org.junit.Assert.assertEquals
import org.junit.*

class PlayGround {
    @Test
    fun playGround() {
        println("Hello Kotlin PlayGround")

        assertEquals(true, true)
    }
}

IntelliJ 右側の Gradle パネルから verification -> test をダブルクリックすると、テストが走って画面下部のテストの出力に Hello Kotlin PlayGround と表示されるはずです。

あとは、このファイルに好き勝手にクラスを足すなりなんなりして、playGround メソッドでいろいろつっつきまわせます。これで Kotlin の言語機能を手軽にお試しできると思います。

ここまで 20 分ぐらい

おわり

というわけで、Kotlin で簡単にコードを書く環境ができました。導入は基本 IDE をインストールするだけというお手軽さです。

Kotlin そのものについてはすでにおもしろい記事があるとおもうので、構築した環境でそれらの記事を読みながら手を動かすと理解が深まるかと思います。

qiita.com

qiita.com

おしまい