cabal init --interactive
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu$ cabal init --interactive
Should I generate a simple project with sensible defaults? [default: y] n
What does the package build:
1) Executable
2) Library
3) Library and Executable
Your choice? 1
What is the main module of the executable:
* 1) Main.hs (does not yet exist, but will be created)
2) Main.lhs (does not yet exist, but will be created)
3) Other (specify)
Your choice? [default: Main.hs (does not yet exist, but will be created)] 1
Please choose version of the Cabal specification to use:
1) 1.10 (legacy)
2) 2.0 (+ support for Backpack, internal sub-libs, '^>=' operator)
3) 2.2 (+ support for 'common', 'elif', redundant commas, SPDX)
* 4) 2.4 (+ support for '**' globbing)
5) 3.0 (+ set notation for ==, common stanzas in ifs, more redundant commas, better pkgconfig-depends)
Your choice? [default: 2.4 (+ support for '**' globbing)]
Package name? [default: haskell-practice-on-ubuntu] chat_server
Couldn't parse chat_server, please try again!
Package name? [default: haskell-practice-on-ubuntu] chat-server
Package version? [default: 0.1.0.0]
Please choose a license:
* 1) NONE
2) BSD-2-Clause
3) BSD-3-Clause
4) Apache-2.0
5) MIT
6) MPL-2.0
7) ISC
8) GPL-2.0-only
9) GPL-3.0-only
10) LGPL-2.1-only
11) LGPL-3.0-only
12) AGPL-3.0-only
13) GPL-2.0-or-later
14) GPL-3.0-or-later
15) LGPL-2.1-or-later
16) LGPL-3.0-or-later
17) AGPL-3.0-or-later
18) Other (specify)
Your choice? [default: NONE] 5
Author name? [default: muzudho]
Maintainer email? [default: [email protected]]
Project homepage URL? https://github.com/muzudho/haskell-practice-on-ubuntu
Project synopsis? for shogi server
Project category:
* 1) (none)
2) Codec
3) Concurrency
4) Control
5) Data
6) Database
7) Development
8) Distribution
9) Game
10) Graphics
11) Language
12) Math
13) Network
14) Sound
15) System
16) Testing
17) Text
18) Web
19) Other (specify)
Your choice? [default: (none)] 13
What base language is the package written in:
* 1) Haskell2010
2) Haskell98
3) Other (specify)
Your choice? [default: Haskell2010]
Add informative comments to each field in the cabal file (y/n)? [default: n]
Guessing dependencies...
Generating LICENSE...
Warning: LICENSE already exists, backing up old version in LICENSE.save0
Generating CHANGELOG.md...
Warning: CHANGELOG.md already exists, backing up old version in CHANGELOG.md.save0
Generating chat-server.cabal...
You may want to edit the .cabal file and add a Description field.
「 しまった プロジェクト・フォルダーの中身が ぶちまけられて ファイルをリネームまで されてしまった、
手動で復元しよ」
🗒 Main.hs
:
-- in Main.hs
module Main where
import Network.Socket
main :: IO ()
main = do
sock <- socket AF_INET Stream 0 -- create socket
setSocketOption sock ReuseAddr 1 -- make socket immediately reusable - eases debugging.
bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
listen sock 2 -- set a max of 2 queued connections
mainLoop sock -- unimplemented
Output:
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu/chat-server$ cabal build
Build profile: -w ghc-9.4.7 -O1
In order, the following will be built (use -v for more details):
- chat-server-0.1.0.0 (exe:chat-server) (file app/Main.hs changed)
Preprocessing executable 'chat-server' for chat-server-0.1.0.0..
Building executable 'chat-server' for chat-server-0.1.0.0..
[1 of 1] Compiling Main ( app/Main.hs, /home/muzudho/Documents/git_hub/haskell-practice-on-ubuntu/chat-server/dist-newstyle/build/x86_64-linux/ghc-9.4.7/chat-server-0.1.0.0/x/chat-server/build/chat-server/chat-server-tmp/Main.o ) [Source file changed]
app/Main.hs:4:1: error:
Could not find module ‘Network.Socket’
Use -v (or `:set -v` in ghci) to see a list of the files searched for.
|
4 | import Network.Socket
| ^^^^^^^^^^^^^^^^^^^^^
「 ↑ 依存性は .cabal
ファイルにどう書けばいいんだ?」
executable chat-server
-- ...
build-depends:
base ^>=4.17.2.0,
network,
chat-server
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu/chat-server$ cabal build
...
app/Main.hs:10:34: error:
Variable not in scope: iNADDR_ANY :: HostAddress
|
10 | bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
| ^^^^^^^^^^
app/Main.hs:12:5: error:
Variable not in scope: mainLoop :: Socket -> IO ()
|
12 | mainLoop sock -- unimplemented
| ^^^^^^^^
「 動かないコードをサンプルにするのは 止めてほしいわねぇ」
-- in Main.hs
module Main where
import Network.Socket
-- メイン
main :: IO ()
main = do
sock <- socket AF_INET Stream 0 -- create socket
setSocketOption sock ReuseAddr 1 -- make socket immediately reusable - eases debugging.
bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
listen sock 2 -- set a max of 2 queued connections
mainLoop sock -- unimplemented
-- メインループ
mainLoop :: Socket -> IO ()
mainLoop sock = do
conn <- accept sock -- accept a connection and handle it
runConn conn -- run our server's logic
mainLoop sock -- repeat
runConn :: (Socket, SockAddr) -> IO ()
runConn (sock, _) = do
send sock "Hello!\n"
close sock
Output:
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu/chat-server$ cabal build
Build profile: -w ghc-9.4.7 -O1
In order, the following will be built (use -v for more details):
- chat-server-0.1.0.0 (exe:chat-server) (file app/Main.hs changed)
Preprocessing executable 'chat-server' for chat-server-0.1.0.0..
Building executable 'chat-server' for chat-server-0.1.0.0..
[1 of 1] Compiling Main ( app/Main.hs, /home/muzudho/Documents/git_hub/haskell-practice-on-ubuntu/chat-server/dist-newstyle/build/x86_64-linux/ghc-9.4.7/chat-server-0.1.0.0/x/chat-server/build/chat-server/chat-server-tmp/Main.o ) [Source file changed]
app/Main.hs:11:34: error:
Variable not in scope: iNADDR_ANY :: HostAddress
|
11 | bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
| ^^^^^^^^^^
app/Main.hs:24:5: error:
Variable not in scope: send :: Socket -> String -> IO a0
Suggested fix: Perhaps use ‘snd’ (imported from Prelude)
|
24 | send sock "Hello!\n"
| ^^^^
「 ↑ 少しずつ説明していくのだろう、それも どうかと思うが」
-- in Main.hs
module Main where
import Network.Socket
-- in the imports our Main.hs add:
import System.IO
-- メイン
main :: IO ()
main = do
sock <- socket AF_INET Stream 0 -- create socket
setSocketOption sock ReuseAddr 1 -- make socket immediately reusable - eases debugging.
bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
listen sock 2 -- set a max of 2 queued connections
mainLoop sock -- unimplemented
-- メインループ
mainLoop :: Socket -> IO ()
mainLoop sock = do
conn <- accept sock -- accept a connection and handle it
runConn conn -- run our server's logic
mainLoop sock -- repeat
-- and we'll change our `runConn` function to look like:
runConn :: (Socket, SockAddr) -> IO ()
runConn (sock, _) = do
hdl <- socketToHandle sock ReadWriteMode
hSetBuffering hdl NoBuffering
hPutStrLn hdl "Hello!"
hClose hdl
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu/chat-server$ cabal build
Build profile: -w ghc-9.4.7 -O1
In order, the following will be built (use -v for more details):
- chat-server-0.1.0.0 (exe:chat-server) (file app/Main.hs changed)
Preprocessing executable 'chat-server' for chat-server-0.1.0.0..
Building executable 'chat-server' for chat-server-0.1.0.0..
[1 of 1] Compiling Main ( app/Main.hs, /home/muzudho/Documents/git_hub/haskell-practice-on-ubuntu/chat-server/dist-newstyle/build/x86_64-linux/ghc-9.4.7/chat-server-0.1.0.0/x/chat-server/build/chat-server/chat-server-tmp/Main.o ) [Source file changed]
app/Main.hs:14:34: error:
Variable not in scope: iNADDR_ANY :: HostAddress
|
14 | bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
| ^^^^^^^^^^
-- in Main.hs
module Main where
import Network.Socket
-- in the imports our Main.hs add:
import System.IO
-- add to our imports:
import Control.Concurrent
-- メイン
main :: IO ()
main = do
sock <- socket AF_INET Stream 0 -- create socket
setSocketOption sock ReuseAddr 1 -- make socket immediately reusable - eases debugging.
bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
listen sock 2 -- set a max of 2 queued connections
mainLoop sock -- unimplemented
-- メインループ
mainLoop :: Socket -> IO ()
mainLoop sock = do
conn <- accept sock -- accept a connection and handle it
forkIO (runConn conn) -- run our server's logic. split off each connection into its own thread
mainLoop sock -- repeat
-- and we'll change our `runConn` function to look like:
runConn :: (Socket, SockAddr) -> IO ()
runConn (sock, _) = do
hdl <- socketToHandle sock ReadWriteMode
hSetBuffering hdl NoBuffering
hPutStrLn hdl "Hello!"
hClose hdl
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu/chat-server$ cabal build
Build profile: -w ghc-9.4.7 -O1
In order, the following will be built (use -v for more details):
- chat-server-0.1.0.0 (exe:chat-server) (file app/Main.hs changed)
Preprocessing executable 'chat-server' for chat-server-0.1.0.0..
Building executable 'chat-server' for chat-server-0.1.0.0..
[1 of 1] Compiling Main ( app/Main.hs, /home/muzudho/Documents/git_hub/haskell-practice-on-ubuntu/chat-server/dist-newstyle/build/x86_64-linux/ghc-9.4.7/chat-server-0.1.0.0/x/chat-server/build/chat-server/chat-server-tmp/Main.o ) [Source file changed]
app/Main.hs:17:34: error:
Variable not in scope: iNADDR_ANY :: HostAddress
|
17 | bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
|
「 ↑ 試しながら 覚えていくわけではないので なんも身につかん。座学」
-- in Main.hs
module Main where
import Network.Socket
-- in the imports our Main.hs add:
import System.IO
-- add to our imports:
import Control.Concurrent
-- in Main.hs
type Msg = String
-- メイン
main :: IO ()
main = do
sock <- socket AF_INET Stream 0 -- create socket
setSocketOption sock ReuseAddr 1 -- make socket immediately reusable - eases debugging.
bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
listen sock 2 -- set a max of 2 queued connections
chan <- newChan -- notice that newChan :: IO (Chan a)
mainLoop sock chan -- pass it into the loop
-- メインループ
mainLoop :: Socket -> Chan Msg -> IO () -- See how Chan now uses Msg.
mainLoop sock chan = do
conn <- accept sock -- accept a connection and handle it
forkIO (runConn conn chan) -- run our server's logic. split off each connection into its own thread. pass the channel to runConn
mainLoop sock chan -- repeat
-- and we'll change our `runConn` function to look like:
runConn :: (Socket, SockAddr) -> IO ()
runConn (sock, _) = do
hdl <- socketToHandle sock ReadWriteMode
hSetBuffering hdl NoBuffering
hPutStrLn hdl "Hello!"
hClose hdl
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu/chat-server$ cabal build
Build profile: -w ghc-9.4.7 -O1
In order, the following will be built (use -v for more details):
- chat-server-0.1.0.0 (exe:chat-server) (file app/Main.hs changed)
Preprocessing executable 'chat-server' for chat-server-0.1.0.0..
Building executable 'chat-server' for chat-server-0.1.0.0..
[1 of 1] Compiling Main ( app/Main.hs, /home/muzudho/Documents/git_hub/haskell-practice-on-ubuntu/chat-server/dist-newstyle/build/x86_64-linux/ghc-9.4.7/chat-server-0.1.0.0/x/chat-server/build/chat-server/chat-server-tmp/Main.o ) [Source file changed]
app/Main.hs:20:34: error:
Variable not in scope: iNADDR_ANY :: HostAddress
|
20 | bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
| ^^^^^^^^^^
「 ↑ なんか Chan
というのを使うようだが さっぱり 意味ワカラン」
app/Main.hs:16:1: error: parse error on input ‘import’
|
16 | import Control.Monad.Fix (fix)
| ^^^^^^
「 違うや、 import
文は type
文より先に書かないといけないみたいだ」
-- in Main.hs
module Main where
import Network.Socket
-- in the imports our Main.hs add:
import System.IO
-- add to our imports:
import Control.Concurrent
-- at the top of Main.hs
import Control.Monad.Fix (fix)
-- in Main.hs
type Msg = String
-- メイン
main :: IO ()
main = do
sock <- socket AF_INET Stream 0 -- create socket
setSocketOption sock ReuseAddr 1 -- make socket immediately reusable - eases debugging.
bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
listen sock 2 -- set a max of 2 queued connections
chan <- newChan -- notice that newChan :: IO (Chan a)
mainLoop sock chan -- pass it into the loop
-- メインループ
mainLoop :: Socket -> Chan Msg -> IO () -- See how Chan now uses Msg.
mainLoop sock chan = do
conn <- accept sock -- accept a connection and handle it
forkIO (runConn conn chan) -- run our server's logic. split off each connection into its own thread. pass the channel to runConn
mainLoop sock chan -- repeat
-- and we'll change our `runConn` function to look like:
runConn :: (Socket, SockAddr) -> Chan Msg -> IO ()
runConn (sock, _) chan = do
let broadcast msg = writeChan chan msg
hdl <- socketToHandle sock ReadWriteMode
hSetBuffering hdl NoBuffering
commLine <- dupChan chan
-- fork off a thread for reading from the duplicated channel
forkIO $ fix $ \loop -> do
line <- readChan commLine
hPutStrLn hdl line
loop
-- read lines from the socket and echo them back to the user
fix $ \loop -> do
line <- fmap init (hGetLine hdl)
broadcast line
loop
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu/chat-server$ cabal build
Build profile: -w ghc-9.4.7 -O1
In order, the following will be built (use -v for more details):
- chat-server-0.1.0.0 (exe:chat-server) (file app/Main.hs changed)
Preprocessing executable 'chat-server' for chat-server-0.1.0.0..
Building executable 'chat-server' for chat-server-0.1.0.0..
[1 of 1] Compiling Main ( app/Main.hs, /home/muzudho/Documents/git_hub/haskell-practice-on-ubuntu/chat-server/dist-newstyle/build/x86_64-linux/ghc-9.4.7/chat-server-0.1.0.0/x/chat-server/build/chat-server/chat-server-tmp/Main.o ) [Source file changed]
app/Main.hs:23:34: error:
Variable not in scope: iNADDR_ANY :: HostAddress
|
23 | bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
| ^^^^^^^^^^
-- Main.hs, final code
module Main where
import Network.Socket
import System.IO
import Control.Exception
import Control.Concurrent
import Control.Monad (when)
import Control.Monad.Fix (fix)
-- メイン
main :: IO ()
main = do
sock <- socket AF_INET Stream 0 -- create socket
setSocketOption sock ReuseAddr 1 -- make socket immediately reusable - eases debugging.
bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
listen sock 2 -- set a max of 2 queued connections
chan <- newChan -- notice that newChan :: IO (Chan a)
_ <- forkIO $ fix $ \loop -> do
(_, _) <- readChan chan
loop
mainLoop sock chan 0 -- pass it into the loop
type Msg = (Int, String)
-- メインループ
mainLoop :: Socket -> Chan Msg -> Int -> IO () -- See how Chan now uses Msg.
mainLoop sock chan msgNum = do
conn <- accept sock -- accept a connection and handle it
forkIO (runConn conn chan msgNum) -- run our server's logic. split off each connection into its own thread. pass the channel to runConn
mainLoop sock chan $! msgNum + 1 -- repeat
-- and we'll change our `runConn` function to look like:
runConn :: (Socket, SockAddr) -> Chan Msg -> Int -> IO ()
runConn (sock, _) chan msgNum = do
let broadcast msg = writeChan chan (msgNum, msg)
hdl <- socketToHandle sock ReadWriteMode
hSetBuffering hdl NoBuffering
hPutStrLn hdl "Hi, what's your name?"
name <- fmap init (hGetLine hdl)
broadcast ("--> " ++ name ++ " entered chat.")
hPutStrLn hdl ("Welcome, " ++ name ++ "!")
commLine <- dupChan chan
-- fork off a thread for reading from the duplicated channel
reader <- forkIO $ fix $ \loop -> do
(nextNum, line) <- readChan commLine
when (msgNum /= nextNum) $ hPutStrLn hdl line
loop
handle (\(SomeException _) -> return ()) $ fix $ \loop -> do
line <- fmap init (hGetLine hdl)
case line of
-- If an exception is caught, send a message and break the loop
"quit" -> hPutStrLn hdl "Bye!"
-- else, continue looping.
_ -> broadcast (name ++ ": " ++ line) >> loop
killThread reader -- kill after the loop ends
broadcast ("<-- " ++ name ++ " left.") -- make a final broadcast
hClose hdl -- close the handle
muzudho@muzudho-MS-7B09:~/Documents/git_hub/haskell-practice-on-ubuntu/chat-server$ cabal build
Build profile: -w ghc-9.4.7 -O1
In order, the following will be built (use -v for more details):
- chat-server-0.1.0.0 (exe:chat-server) (file app/Main.hs changed)
Preprocessing executable 'chat-server' for chat-server-0.1.0.0..
Building executable 'chat-server' for chat-server-0.1.0.0..
[1 of 1] Compiling Main ( app/Main.hs, /home/muzudho/Documents/git_hub/haskell-practice-on-ubuntu/chat-server/dist-newstyle/build/x86_64-linux/ghc-9.4.7/chat-server-0.1.0.0/x/chat-server/build/chat-server/chat-server-tmp/Main.o ) [Source file changed]
app/Main.hs:17:34: error:
Variable not in scope: iNADDR_ANY :: HostAddress
|
17 | bind sock (SockAddrInet 4242 iNADDR_ANY) -- listen on TCP port 4242.
|
「 ↑ Network.Socket
にありそうなもんだけどな」
「 定数で 0 の意味らしいから iNADDR_ANY
は 0 に置き換えたろ」
cabal run
$ telnet localhost 4242
Output:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hi, what's your name?
muzudho
Welcome, muzudho!
quit
Bye!
Connection closed by foreign host.
「 ↑ じゃあ このプログラムを改造すれば クライアント・サーバー型の通信ができるんだ」
.
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント