haskell-jp / questions #24

たとえば、19ページ。関数適用は、tail call (すなわち jump)とありますね。
誰か忘れましたが、日本人の方がたくさん論文を読んでまとめのレポート書いてました。内容には一部間違いがありましたが。。。
なんか書いてました。
ありがとうございます!
VSCodeでHaskell IDE engineを使用していると気づいたら5,6GBもメモリを消費しているのですが、これを回避する方法はありますか。いまのところは定期的にIDEを再起動してこれを回避しています。
なるほど。ありがとうございます。
特定のインスタンスを持っている場合だけ処理を変えたいのですが、なにかやり方はありますでしょうか?
具体的には、
fromIntegral :: (Num a, Integral i) => i -> a

を拡張して、 (Floating a, Bounded i) の場合だけ、ノーマライズする処理を入れたいです。

気分的にはこんなコードを動くようにしたいです。

class FromIntegralNormalized i a where
  fromIntegralNormalized :: i -> a

instance (Integral i, Num a, Bounded i, Floating a) => FromIntegralNormalized i a where
  fromIntegralNormalized = (/ fromIntegral (maxBound :: i)) . fromIntegral

instance (Integral i, Num a) => FromIntegralNormalized i a where
  fromIntegralNormalized = fromIntegral
基本的には,これをそのまま実現するのは難しいため,APIの見直しを推奨します.

APIそのままとはいきませんが,基本的にはGADTsを使って
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}

data WhetherNormalize i a where
  BoundedNormalize :: (Bounded i, Floating a) => i -> WhetherNormalize i a
  WithoutNormalize :: i -> WhetherNormalize i a

fromIntegralWithNormalize :: forall i a. (Integral i, Num a) => WhetherNormalize i a -> a
fromIntegralWithNormalize (BoundedNormalize x) = fromIntegral x / fromIntegral (maxBound :: i)
fromIntegralWithNormalize (WithoutNormalize x) = fromIntegral x

というのがいいと思います.

このような関数を多相化する場合,実行時まで制約が存在するかの判定を遅延する必要があります(要は,それぞれの制約をMaybeに包むことで多相を実現するイメージです)
なお,一応この仕組みを単純に実現するコンパイラプラグインが提供されているみたいです
https://hackage.haskell.org/package/constraints-emerge
OVERLAPSプラグマを使えば近い事は可能です。ただし、型クラス制約が増えただけではOVERLAPSにできる条件「より具体的なインスタンス」を満たさないので、オーバーラップさせる型は個別に指定する必要があります。

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables #-}

import 

class FromIntegralNormalized i a where
  fromIntegralNormalized :: i -> a

boundedToFloating :: forall i a. (Integral i, Bounded i, Num a, Floating a) => i -> a
boundedToFloating = (/ fromIntegral (maxBound :: i)) . fromIntegral

instance {-# OVERLAPS #-} (Integral i, Bounded i) => FromIntegralNormalized i Float where
  fromIntegralNormalized = boundedToFloating

instance {-# OVERLAPS #-} (Integral i, Bounded i) => FromIntegralNormalized i Double where
  fromIntegralNormalized = boundedToFloating

instance (Integral i, Num a) => FromIntegralNormalized i a where
  fromIntegralNormalized = fromIntegral

main = do
  print (fromIntegralNormalized (64 :: Int8) :: Int)
  print (fromIntegralNormalized (64 :: Int8) :: Float)
  print (fromIntegralNormalized (64 :: Int8) :: Double)
なるほど、この辺は手で書くしかないんですね。ありがとうございます。
この記事は両方ともとても参考になります!ありがとうございます。
@mizunashi-mana @as_capabl ありがとうございます!やってみます!
OVERLAPS の方法だと、呼び出し元に (Num a) の制約しかなかったら、ノーマライズしてくれないですね。

hoge :: (Num a) => a
hoge = fromIntegralNormalized (100 :: Word8)

>>> hoge :: Int
100
>>> hoge :: Float
100.0
 
正攻法で GADTs を使う方法でやることにして、こんな感じになりました。

data Normalizing i a where
  NonNormalized :: Normalizing i a
  Normalized :: (Bounded i, Fractional a) => Normalizing i a

fromIntegral' :: forall i a. (Integral i, Num a) => Normalizing i a -> i -> a
fromIntegral' NonNormalized = fromIntegral
fromIntegral' Normalized    = (/ fromIntegral (maxBound :: i)) . fromIntegral
constraints-emerge のプラグインも試してみましたが、呼び出し元に Emerge 制約が伝播していくのが気になるので見送りました。
@a_kirisaki has joined the channel
GraphQLのクエリを作ってくれるモジュールに心当たりのある方はいますか?サーバを提供するものなら発見したのですが……
これHaskell強い人のigrepさんとlotzさんが知らないということはやはり存在しないのでは……
一応あるにはあるっぽいんですが、メンテされてなくてちょっとプロジェクトに入れにくいんですよね:graphql: Haskell GraphQL implementation - https://hackage.haskell.org/package/graphql
うお~~~ってなりながら自前で作ることしました
例えば
type Key = String
type Value = String
data Hoge = [(Key, Value)] 

みたいな型があったとして、`Key`が重複していたらコンパイル時にエラーを出すような仕組みって作れるんでしょうか。`TemplateHaskell`を利用したらできるんでしょうか。
もうひとつ、次のような型があったとして
data Member = Name String | Arguments [Argument]
data Field a = Field a [Member]

Field a (例えば Field Int )をとって Int か、他の型に包んだ Some Int のような型を生成できるのでしょうか。 Generics とか使えばできるのかなとは考えてみたのですが上手くまとまらないです。(完全にGraphQLのAST作ってそこから型作る話です)
どんな Key があるのかコンパイル時にすべて求められるのであれば、可能です。
Extensible Recordというのはみんなそうしたものです。
fumiさんのextensibleでも、キーが重複していれば検出してくれます。
まぁ、そこまで検索しづらいワードでもないでしょうから、検索してないならないってことなんでしょうね...
:thinking_face:
Foo Int から IntSome Int を生成するというのがよくわからないです。
型ではなくてそういう関数を自動生成したいという話ですかね?
ありがとうございます。やはりextensibleですね
ちょっといい方が適当すぎました。前述の型があるとき
data Nyaan a = Nyaan a

みたいな型があったとしてこの a の型を Field a の型と一致させるような仕組みが作れないかなということです
すみません、ますますわからない... :disappointed:
ちょっと質問で返しちゃいますが、
もともとのゴールは、GraphQLをHaskellに生成(してサーバーに送って、結果をHaskellの型に変換)させたいわけですよね。
どういうアプローチでやろうとしてます?
例えば、 https://github.com/haskell-graphql/graphql-api#roadmap
Derive client implementations from types
と計画しているように型からクエリーを組み立てる関数を生成するっていうアプローチではない?
ちょっと自分でも整理しきれてないのでいろいろ試してからまた来ます!すみません!
Hakyllのビルドがメモリ不足で失敗するのですが何か良い対策はありませんか?Hakyllそのものに手を入れて二つのライブラリへ分割してコンパイルさせることで対処していますが、あとあと困りそうで……
travis がメモリ不足で死ぬ時は -j 1 オプションで回避したことがありますが、ちょっと違う感じですか?
https://haskell.e-bigmoon.com/posts/2017/12-31-travis-out-of-memory.html
なるほど……手元のパソコンでビルドした時に発生するものなので違いますね。具体的にはこのバグに引っかかっていて、どうしようもないのでメモリ使用量を減らす方法はないか、という感じです
https://github.com/jaspervdj/hakyll/issues/613
Couldn’t repro on stackage nightly (GHC 8.4).

windows 環境が無いので確かめられないのですが、コメントにある ghc-8.4 系の lts-12.0 でもやっぱりダメな感じですか?
ああ、頭から抜けていました!できました!ありがとうございます。
unlift.ioの対応でresourcetのインスタンスにstatetを受け付けなくなってますが、
どのように修正するのがおすすめでしょうか。
http://hackage.haskell.org/package/conduit-find
(リンク張り間違えていました。find-conduitでなくてこっちでした。)
ちなみにこれを使いたいです。
直接の回答でなくて恐縮ですが、UnliftIOはそもそもmonad-controlと違ってStateTを使えなくしているのが売りだったはずです。
https://www.fpcomplete.com/blog/2017/06/readert-design-pattern でも少し触れています。
するとstatetつかっているようなものは
readertとioref/tvarとかの組み合わせにかえるのがいいのでしょうか。
そうなりますね。ちょっと書き換えが面倒くさそうですが...
結構めんどくさいというか、どうすればできるのだっけという感じがしております
何回も申し訳ありませんが質問です。 Data.Extensible のレコードから Web.Internal.FormUrlEncodedForm に変換する以下のようなコードで
{-# LANGUAGE AllowAmbiguousTypes         #-}
{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE FlexibleContexts         #-}
{-# LANGUAGE FlexibleInstances         #-}
{-# LANGUAGE MultiParamTypeClasses         #-}
{-# LANGUAGE OverloadedStrings         #-}
{-# LANGUAGE ScopedTypeVariables         #-}
{-# LANGUAGE TypeOperators     #-}
{-# LANGUAGE UndecidableInstances     #-}
{-# LANGUAGE UndecidableSuperClasses #-}


module Hrafnar.Service.Common
  ( AwaitRequest
  , Server
  ) where

import           Data.Extensible
import           Data.HashMap.Strict as HM
import           GHC.TypeLits
import           Web.Internal.FormUrlEncoded

instance Forall (KeyValue KnownSymbol (Instance1 ToFormKey h)) xs => ToForm (Field h :* xs) where
  toForm = Form . hfoldlWithIndexFor
    (Proxy :: Proxy (KeyValue KnownSymbol (Instance1 ToFormKey h)))
    (\k m v -> HM.insert (toFormKey $ symbolVal $ proxyAssocKey k)  [toFormKey v] m)
    HM.empty

としたのですがどうしても
    • Could not deduce (ToFormKey (Field h x))
        arising from a use of ‘toFormKey’
      from the context: Forall
                          (KeyValue KnownSymbol (Instance1 ToFormKey h)) xs
        bound by the instance declaration
        at src/Hrafnar/Service/Common.hs:33:10-91
      or from: KeyValue KnownSymbol (Instance1 ToFormKey h) x
        bound by a type expected by the context:
                   KeyValue KnownSymbol (Instance1 ToFormKey h) x =>
                   Membership xs x
                   -> HashMap Text [Text] -> Field h x -> HashMap Text [Text]
        at src/Hrafnar/Service/Common.hs:(34,19)-(37,12)
    • In the expression: toFormKey v
      In the second argument of ‘HM.insert’, namely ‘[toFormKey v]’
      In the expression:
        HM.insert (toFormKey $ symbolVal $ proxyAssocKey k) [toFormKey v] m

というエラーが出て悩まされています。お心当たりありませんか
あ、stack lts-9.12です
おそらくですけど
ToJson のインスタンスとかを参考にしたと思うんですが https://hackage.haskell.org/package/extensible-0.4.10/docs/src/Data.Extensible.Dictionary.html#line-216
こんな風に Instance1 でいっきにやるには Field h kv のインスタンスも必要なので、 instance ToFormKey (h (AssocValue kv)) => ToFormKey (Field h kv) も定義すると通ると思います
とりあえず
instance ToFormKey (h (AssocValue kv)) => ToFormKey (Field h kv) where
  ToFormKey = undefined

として適当な Record を作り toForm してみたのですが
   • No instance for (ToFormKey (Identity Text))
        arising from a use of ‘toForm’
 

と出てしまいます。また、 extensible のソース見ながら
deriving instance ToFormKey (h (AssocValue kv)) => ToFormKey (Field h kv)

としたところで
  • Can't make a derived instance of ‘ToFormKey (Field h kv)’:
        ‘ToFormKey’ is not a standard derivable class (Eq, Show, etc.)
        Try GeneralizedNewtypeDeriving for GHC's newtype-deriving extension
  

と出たのでコンパイラに従い拡張を入れたところでも同様のエラーが出ました
アッインスタンス作ってないのでは
1個前の発言は、なんかレコード構文のGenericな導出とごっちゃになって instance ToForm HogeType とやってみたけどダメでした
Identityのインスタンスも作る必要があります。また、GeneralisedNewtypeDerivingが本当に有効になっているかもう一度確認してみてください。PolyKindsもおそらく必要です