$30 off During Our Annual Pro Sale. View Details »

入門 Bubble Tea

motemen
June 11, 2022

入門 Bubble Tea

motemen

June 11, 2022
Tweet

More Decks by motemen

Other Decks in Technology

Transcript

  1. ೖ໳ Bubble Tea
    2022
    -
    0 6
    -
    1
    1
    motemen Kyoto.go LT

    View Slide

  2. #ͱ͸

    View Slide

  3. charmbracelet/
    bubbletea
    Go TUI




    The fun, functional and stateful way
    to build terminal apps. A Go framework
    based on The Elm Architecture.


    View Slide

  4. The Elm Architecture

    View Slide

  5. Elm ݴޠ
    https://elm-lang.org/


    Haskell JavaScript


    Redux Prior Art


    https://redux.js.org/understanding/history-and-design/prior-
    art#elm

    View Slide

  6. The Elm Architecture
    • Model: ΞϓϦέʔγϣϯͷεςʔτ


    • View: Model ͔Β HTML Λੜ੒


    • Update: Msg ʹج͖ͮ Model Λߋ৽


    • Msg ͸ϢʔβೖྗͳͲϞσϧͷ֎෦
    View()
    Update(msg)
    Model
    Msg

    View Slide

  7. 1 ࣮૷ͯ͠ΈΔ: counter

    View Slide

  8. interface tea.Model
    // Model contains the program's state as well as its core functions.
    type Model interface {
    // Init is the first function that will be called. It returns an optional
    // initial command. To not perform an initial command return nil.
    Init() Cmd
    // Update is called when a message is received. Use it to inspect messages
    // and, in response, update the model and/or send a command.
    Update(Msg) (Model, Cmd)
    // View renders the program's UI, which is just a string. The view is
    // rendered after every Update.
    View() string
    }

    View Slide

  9. model, View() string
    type model struct {
    count int
    }
    func (model) Init() tea.Cmd {
    return nil
    }
    func (m model) View() string {
    return fmt.Sprintf("count: %v", m.count)
    }

    View Slide

  10. Update(tea.Msg) (tea.Model, tea.Cmd)
    func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
    switch msg.String() {
    case " ":
    m.count = m.count + 1
    return m, nil
    }
    }
    return m, nil
    }
    ߋ৽͞ΕͨϞσϧΛฦ͢
    View() ͕ը໘ʹඳը͞ΕΔ

    View Slide

  11. Msg ͸Ϣʔβʗ֎ք͔Βͷೖྗ
    Msg contain data from the result of a IO operation. Msgs trigger the update function and,
    henceforth, the UI.


    tea.KeyMsg


    tea.MouseMsg


    tea.WindowMsg


    Msg
    type Msg interface{}

    View Slide

  12. bubbletea ʹ৐ͤΔ 🚀
    func main() {
    prog := tea.NewProgram(model{count: 0})
    err := prog.Start()
    if err != nil {
    log.Fatal(err)
    }
    }

    View Slide

  13. ऴྃͰ͖ΔΑ͏ʹ͢Δ
    func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
    switch msg.String() {
    case "q", "ctrl+c":
    return m, tea.Quit
    case " ":
    m.count = m.count + 1
    return m, nil
    }
    }
    return m, nil
    }
    CmdΛฦ͢

    View Slide

  14. 2 tea.Cmd Λ࢖ͬͯΈΔ

    View Slide

  15. tea.Cmd
    • “Cmd is an IO operation that
    returns a message when it's
    complete”


    • ֎ք΁ͷ໋ྩ


    • ऴΘͬͨΒ Msg Λ໭ؔ͢਺


    • goroutine Ͱ࣮ߦ͞ΕΔ
    View()
    Cmd
    Update(msg)
    Model
    Msg

    View Slide

  16. HTTP ௨৴Λ൐͏ྫ
    type countLoadedMsg struct {
    count int
    }
    func (m model) hitCounter() tea.Msg {
    count, err := m.api.Hit()
    if err != nil {
    panic(err)
    }
    return countLoadedMsg{count: count}
    }
    var _ tea.Cmd = model{}.hitCounter
    ͜ͷϝιουͷܕ͕ tea.Cmd
    IO͕ऴΘͬͨΒMsgΛฦ͢

    View Slide

  17. ΦϦδφϧͷ Msg Λड͚औΔ
    func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
    switch msg.String() {
    case " ":
    m.loading = true
    return m, m.hitCounter
    }
    case countLoadedMsg:
    m.count = msg.count
    m.loading = false
    return m, nil
    }
    return m, nil
    }
    CmdΛฦ͢
    MsgΛड͚औͬͨΒϞσϧߋ৽

    View Slide

  18. Init(), View()
    func (m model) Init() tea.Cmd {
    return m.hitCounter
    }
    func (m model) View() string {
    if m.loading {
    return "..."
    } else {
    return fmt.Sprintf("count: %v", m.count)
    }
    }
    ϓϩάϥϜ։࢝࣌ʹൃߦ͍ͨ͠Cmd

    View Slide

  19. 3 bubbles ΋࢖ͬͯΈΔ

    View Slide

  20. bubbles ࢖ͬͨྫ
    type model struct {
    ...
    spinner spinner.Model
    }
    func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    var cmd tea.Cmd
    m.spinner, cmd =
    m.spinner.Update(msg)
    ...
    }
    func (m model) View() string {
    if m.loading {
    return m.spinner.View()
    }
    ...
    }
    func (m model) Init() tea.Cmd {
    return tea.Batch(
    m.hitCounter,
    m.spinner.Tick,
    )
    }
    • charmbracelet/bubbles


    • bubbletea ༻ͷ TUI ίϯ
    ϙʔωϯτू
    ModelΛೖΕࢠʹ
    ࢠڙʹ΋MsgΛ౉͢
    Ξχϝʔγϣϯ༻Cmd

    View Slide

  21. github.com/motemen/example-go-bubbletea
    The Go gopher is designed by Renee French, licensed under CC BY 3.0

    View Slide