haskell-jp / questions #58

type PersonParams (f :: * -> *) (g :: * -> *) =
  Field Identity :*
  '[ "name" >: f Text
   , "age" >: g Int
   ]


type PersonRequiredParams (f :: * -> *) =
  Field f :*
  '[ "name" >: Text
   ]

type PersonOptionalParams (g :: * -> *) =
  Field g :*
  '[ "age" >: Int
   ]
h *: xsxs のなかに入っているファンクタをフィルターしながら h の方に上げたいのです。
なるほど、勘違いしてたw
例えば PersonParams -> PersonOptionalParams であれば Nullable の wrench に近いやもしれません。
http://hackage.haskell.org/package/extensible-0.5/docs/Data-Extensible-Nullable.html#v:wrench
参考になるかも
wrenchh に作用するだけなので、 xs が不変でないと使えないのです。。。
難しいのは xs が変わってしまう点ですね。
気分的にはこんな感じのコンストレイントが書けないかな、と。
(xs ~ [f x1, g x2, f x3], ys ~ [x1, x3], Include xs (f * ys))
$ ghci
>>> :set -XOverloadedLabels
>>> import Lens.Micro
>>> a = liftRecord person :: PersonRequiredParams
>>> a ^. #name
"alice"
おお、ありがとうございます。見てみます。
今更ながら。開発中の古い資料なんで実際はどうか分かりませんが、こんなのがありました。
当初はビルド時の入力のハッシュ値をとるというプランだったみたいですが、なんかいろいろ試行錯誤してたみたいですね…

https://www.haskell.org/wikiupload/5/50/Hiw2012-philipp-schuster.pdf
aeson についてなんですが

Foo型を表すJSONとして

{ "a":100, "b":200}

が与えられる場合

data Foo = Foo { a :: Int, b :: Int} deriving Generic
instance FromJSON Foo


のようにして簡単に実装できるのですが。
レコード b が省略されているときはb に50が入ってるものだと過程する。
みたいな場合はbの型をMaybe Int にするしかないのでしょうか?
もちろん手書きすればいいのはわかるのですが。
デフォルト値を与えられたりしないでしょうか?
一つの型では難しいんじゃないっすかね。
一旦 Maybe Int として読んでから変換するとか。
:bulb: あとは FomJSON を別途実装した newtype で囲うとか

newtype Default50 = Default50 :: Int
instance FromJSON Default50 where
  parseJSON = withScientific ...
fmfm Maybe Int 経由するのが無難ですかね?:sweat: まぁしょうがないか。 回答どうもです!
あ。 newtype 使う作戦もありなのか。そっちにしよっと :smile:
Stackoverflowに関係ありそうな質問がありました。FromJSONとデフォルトの値を持つ型クラスを満たすnewtypeでラップするという戦略のようです。
https://stackoverflow.com/questions/26683693/aeson-generics-with-default-values
完全にオーバーウェポンって感じだけど extensible の Nullable を駆使すればそれっぽいのを実現できる気がする
MaybeRecordOf 作って各フィールドごとに <|> を適用させるってのをやってましたね。そんで最後に htraverseNothing が1つでもあれば全体として mzero になるみたいな
$ ghci
>>> :set -XOverloadedStrings
>>> import Data.Aeson
>>> decode "{ \"a\": 1, \"b\": 2 }" :: Maybe Foo
Just (a @= 1 <: b @= 2 <: nil)
>>> decode "{ \"a\": 1 }" :: Maybe Foo
Nothing
>>> decode "{ \"a\": 1 }" :: Maybe Foo'
Just (Nullable {getNullable = Just (a @= 1)} <: Nullable {getNullable = Nothing} <: nil)
>>> fromNullable defaultFoo <$> decode "{ \"a\": 1 }" :: Maybe Foo
Just (a @= 1 <: b @= 50 <: nil)
ただデフォルト値なしはできないけど。。。もう少し工夫すればできる気もするが
defaultFooもNullableにして、両方Nothingだったら失敗するようにもできそうです(さらなる追い討ち)
なるほど~かしこい
お世話になります。初歩的なことかもしれませんが、以下質問させてください。

以下のようなクラス宣言およびインスタンス宣言を考えます。
data Fuga a Int = Fuga Int

class Hoge h where
  func :: h a -> a

instance Hoge (Fuga a) where
  func (Fuga x) = ...


ここで、型変数 a に応じて func の振る舞いを変更するには、どのようにしたらよいでしょうか?
具体的なモチベーションは、
ヒープを実装しようとした時に、最小値を取得できるヒープと最大値を取得できるヒープを別の型として表現したいが、実装(もしくはインスタンス宣言)は共通としたい、
というものです。
(ここでは、別の型であることを表現する手段として幽霊型を用いようとしています)
単純に FlexibleInstances 拡張を有効にして2つのインスタンス宣言を書く、というのも手ですが、そうするとほとんど同じ実装(値の比較以外)のインスタンス宣言が2つ出てきてしまいます。
そういうのは避けて、 func の実装を別途 a ごとに用意しておいて、インスタンス内部で a に応じて適切な実装が選択されるようにできればいいなー、と。
a をまた別の型クラスのインスタンスにして比較する処理をメソッドとしてもたせるとかでしょうか :thinking_face:
(質問の意図が組みきれず途中で迷走してしまいましたがそれっぽいものを書いてみました https://repl.it/@lotz84/IvoryRipeAngle
要素の比較方法を変えたい、というものであれば、 Down という newtype でくるんであげることでも可能ですが... https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Ord.html#t:Down
さすがにextensibleへの拡張は考えてませんでしたが。今後の拡張性のため考慮に入れておくべきですね
最初に実際のソースコードをおいておけばよかったですね...
https://gist.github.com/chupaaaaaaan/c70ab641df6f53b35b35e2d9abbf41c3
やりたかったことは、まさに<@U4KPPQW7K> さんの実装がドンピシャ!という感じです。
要素の比較、というよりは、型コンストラクタにだけ現れて値コンストラクタの型変数として現れない型をどの様に実装に取り込むか、に悩んでいました。ありがとうございます。
(ドンピシャ、と言ったけどちょっとわからないところがある…)
最初の一歩としては、単純にそういう FromJSON のインスタンスを定義する、というのがよいと思います。
import Data.Aeson

data Foo = Foo { a :: Int, b :: Int}
  deriving Show

instance FromJSON Foo where
  parseJSON = withObject "Foo" $ \o -> do
    a <- o .: "a"
    b <- o .:? "b" .!= 50
    pure $ Foo a b

-- |
-- >>> run
-- Just (Foo {a = 100, b = 200})
-- Just (Foo {a = 100, b = 50})
run :: IO ()
run = do
  print (decode "{ \"a\":100, \"b\":200}" :: Maybe Foo)
  print (decode "{ \"a\":100}" :: Maybe Foo)
@ 型に依存する部分だけ型クラスで分ければよいのではないでしょうかね?

empty :: (Ord a, h ~ LeftistHeap x) => h a
empty = E

isEmpty :: (Ord a, h ~ LeftistHeap x) => h a -> Bool
isEmpty E = True
isEmpty _ = False

insert :: (Ord a, h ~ LeftistHeap x, Heap h) => a -> h a -> h a
insert x h = merge (T 1 x E E) h

find :: (Ord a, h ~ LeftistHeap x) => h a -> Maybe a
find E = Nothing
find (T _ x _ _) = Just x

delete :: (Ord a, h ~ LeftistHeap x, Heap h) => h a -> Maybe (h a)
delete E = Nothing
delete (T _ _ a b) = Just (merge a b)


class Heap h where
  merge :: Ord a => h a -> h a -> h a

instance Heap (LeftistHeap Min) where
  merge h E = h
  merge E h = h
  merge h1@(T _ x a1 b1) h2@(T _ y a2 b2)
    = if x <= y
      then makeT x a1 $ merge b1 h2
      else makeT y a2 $ merge h1 b2

instance Heap (LeftistHeap Max) where
  merge h E = h
  merge E h = h
  merge h1@(T _ x a1 b1) h2@(T _ y a2 b2)
    = if x >= y
      then makeT x a1 $ merge b1 h2
      else makeT y a2 $ merge h1 b2
わからない箇所が解消しました。
Proxyの使い方がいまいちわかっていなかったですが、具体的な値を定義せずに型を使いたい時に使用するのですね。ありがとうございます。
@kayhide 最初はそれも考えましたが、Heap構造に対する抽象的な操作として他のメソッドも定義しているので、できれば他のメソッドもHeap型クラスに含めたいですね…(現在「純粋関数型データ構造」を読んでいて、それにいくつかのHeap構造が出てくるのでそれに合わせたい、というのもあります)
もちろん、 LeftistHeap に固有の操作は型クラスに含めず、独立に定義するものだと思います。
@atsushi130 has joined the channel
@Synm has joined the channel
お世話になります。とても初歩的な質問で申し訳ないのですが、上の式が成り立っている意味がよくわかりません。
ぜひ教えていただけたら幸いです。よろしくお願いします。
(*), (+) :: Num a => a -> a-> a

ですので (*)b 型を代入すると b -> b になります。 b(+) だとすると (a -> a-> a) -> (a-> a-> a) になります。結合性から右のかっこは省略しても良いので (a -> a-> a) -> a -> a -> a となります
(*)(+) も、それぞれ Num 型クラスのインスタンスであればどれでも受け取るようになっています。
なので、 :t の結果が (Num a, Num (a -> a -> a)) => ... となっているとおり、これらの条件を満たす型が 本当にあれば この式も普通に型チェックを通ります。

ただ、実際のところ (Num a, Num (a -> a -> a)) を満たす型は意図的に、頑張って作らない限りは存在しないので、いざ ((*) (+)) を使おうとしたその時に型エラーになる可能性が高いです。
例えば以下のように:

> ((*) (+)) (+) 1 2 3 :: Integer

<interactive>:4:1: error:
    ? No instance for (Num
                         ((Integer -> Integer)
                          -> (Integer -> Integer) -> Integer -> Integer))
        arising from a use of ‘*’
        (maybe you haven't applied a function to enough arguments?)
    ? In the expression: ((*) (+)) (+) 1 2 3 :: Integer
      In an equation for ‘it’: it = ((*) (+)) (+) 1 2 3 :: Integer

<interactive>:4:6: error:
    ? No instance for (Num (Integer -> Integer))
        arising from a use of ‘+’
        (maybe you haven't applied a function to enough arguments?)
    ? In the first argument of ‘(*)’, namely ‘(+)’
      In the expression: ((*) (+)) (+) 1 2 3 :: Integer
      In an equation for ‘it’: it = ((*) (+)) (+) 1 2 3 :: Integer
なるほど...
助かりました。ありがとうございます
それほど頑張らなくても,あまり違和感なく定義できそう.
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
instance Num a => Num (a -> a -> a) where
  (f + g) x y = f x y + g x y
  (f * g) x y = f x y * g x y
  (f - g) x y = f x y - g x y
  abs f x y = abs (f x y)
  signum f x y = signum (f x y)
  fromInteger i = const (const (fromInteger i))

とかどうですかねぇ.
@qryxip has joined the channel
こんにちは。いつもお世話になっています。

GitHub + CircleCI + Coverall の組み合わせで、CoverallのHaskell対応パッケージで、stackベースのものをお使いの方いらっしゃいますか?

Coverallは、PR送るときにテストカバレッジをチェックしてくれるものですが、Coverall のHaskell対応のパッケージはCabal用でメンテナンスもされていない模様で不安。stack用のCoverallパッケージはいくつかあるみたいなので どれがよいのか戸惑っています。
@shiratori has joined the channel
あ、チャンネルをまちがえた