2019/12/03

Nimで列挙型と構造体を上手く組み合わせて使う

Nimの列挙型と構造体

Nimでは列挙型も構造体も共にtypeを使って以下のように作成します。

------
列挙型
------
type
  MyEnum = enum
    A, B, C
    
------
構造体
------
type
  MyStruct = ref object
    fieldA: int
    fieldB: string

そしてこれらは組み合わせて使うことも可能で、ちょっと複雑な感じになりますが、例えばCDプレーヤーとDVDプレーヤー(昭和感たっぷりで申し訳ございません)というヴァリアントを持ったDeviceという列挙型を作成するとします。
そしてそれぞれ扱うものは違っても、playstopといった同じ名前の機能を持っていています。
その場合は以下のような形で書くことによって実現できます。


type
  DeviceKind = enum
    CDPlayer
    DVDPlayer
  Device = ref object
    case kind: DeviceKind
    of CDPlayer: cd: string
    of DVDPlayer: dvd: string

proc play(d: Device) =
  case d.kind
  of CDPlayer: echo "PLAY: " & d.cd
  of DVDPlayer: echo "PLAY: " & d.dvd

proc stop(d: Device) =
  echo "STOP: " & $d.kind

var
  cdp = Device(kind: CDPlayer, cd: "Buena Vista Social Club")
  dvdp = Device(kind: DVDPlayer, dvd: "Forrest Gump")

proc main() =
  cdp.play()
  dvdp.play()

  cdp.stop()
  dvdp.stop()

when isMainModule:
  main()
playstopといった同じ名前の機能は引数の型をDeviceにすることによって、CDPlayerでもDVDPlayerでも使うことができるようになります。
もちろん以下のような形でも可能です。

type
  Device = ref object of RootObj
    software: string
  CDPlayer = ref object of Device
  DVDPlayer = ref object of Device
  
proc play(d: Device) =
  echo d.software
  
proc stop(d: Device) =
  echo "STOP"
  
proc main() =
  var
    cdp = new CDPlayer
    dvdp = new DVDPlayer
  
  cdp.software = "Buena Vista Social Club"
  dvdp.software = "Forrest Gump"

  cdp.play()
  dvdp.play()

  cdp.stop()
  dvdp.stop()
  
when isMainModule:
  main()
好みの問題かもしれませんが、細かい部分でちょっと納得いかない感じになります。
果たしてどちらが正解なのだろうか。個人的には前者の使い方がいい感じなように思います。