Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

oneof handling #255

Closed
haskie-lambda opened this issue Nov 12, 2020 · 23 comments
Closed

oneof handling #255

haskie-lambda opened this issue Nov 12, 2020 · 23 comments
Assignees
Labels
question Further information is requested rpc

Comments

@haskie-lambda
Copy link

haskie-lambda commented Nov 12, 2020

tags: grpc, question

I am playing around with the library and came across something I am not capable of handling:

The example protocol I am playing around with has the following message defined:

message Options {
    oneof inferOpt {
        bool infer = 1;
    }
    oneof explainOpt {
       bool explain = 2;
    }
    oneof batchSizeOpt {
        int32 batchSize = 3;
    }
}

and i cannot figure out, how to represent the oneof field as the corresponding datatype so that i can derive FromSchema & ToSchema

data Options =
     Options { inferOpt :: [Bool]
             , explainOpt :: [Bool]
             , batchSizeOpt :: [Int32] }
     deriving ( Generic
              , ToSchema   GraknSchema "Options"
              , FromSchema GraknSchema "Options" )

i tried using a list as above, a Maybe, an Either x Void an Either x (); I am running out of ideas how to represent that.

I was also unable to find documentation on how to handle oneof values;

Can you point me to some documentations for this detail or to the right way of handling it?

just on a sidenote if this sounds familiar to anybody: i originally posted this on reddit but got no answers

@kutyel kutyel added question Further information is requested rpc labels Nov 12, 2020
@kutyel
Copy link
Member

kutyel commented Nov 12, 2020

Hey @faeblDevelopment, thanks for your question and for giving mu-haskell a try! 😄 I think I remember @cb372 having some kind of example with oneof if I remember correctly, otherwise I'm sure @serras will know how to answer your question shortly 😉

@serras
Copy link
Collaborator

serras commented Nov 12, 2020

I think the problem is that oneof specifies one of the choices, whereas the type you've built is a record of the three elements. Could you try with the following?

data Options
  = OptionsInfer { infer :: [Bool] }
  | OptionsExplain { explain :: [Bool] }
  | OptionsBatchSize { batchSize :: [Int32] }

@haskie-lambda
Copy link
Author

Thanks so much for the fast replies;

i tried it with @serras solution;
however this results in the (i guess expected) error message

/src/Proto.hs:41:17: error:
    • Could not find field "batchSizeOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
41 |      deriving ( ToSchema   GraknSchema "Options" )
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/src/Proto.hs:41:17: error:
    • Could not find field "explainOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
41 |      deriving ( ToSchema   GraknSchema "Options" )
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/src/Proto.hs:41:17: error:
    • Could not find field "inferOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
41 |      deriving ( ToSchema   GraknSchema "Options" )
   |  

i guess that this happens because the fields in the haskell data type are upper case;
so i tried it with the following:

type InferOptMapping = '[ "InferOpt" ':-> "inferOpt"
                        , "ExplainOpt" ':-> "explainOpt"
                        , "BatchSizeOpt" ':-> "batchSizeOpt" ]
type InferOptMapping' = '[ "inferOpt" ':-> "InferOpt"
                         , "explainOpt" ':-> "ExplainOpt"
                         , "batchSizeOpt" ':-> "BatchSizeOpt" ]

data Options
  = InferOpt { infer :: [Bool] }
  | ExplainOpt { explain :: [Bool] }
  | BatchSizeOpt { batchSize :: [Int32] }
     deriving ( Generic )
     deriving ( ToSchema   GraknSchema "Options" )
        via ( CustomFieldMapping "Options" InferOptMapping' Options)
     deriving ( FromSchema GraknSchema "Options" )
        via ( CustomFieldMapping "Options" InferOptMapping Options)

and with InferOptMapping and InferOptMapping' flipped

but get the following error message for that:

/src/Proto.hs:41:17: error:
    • Could not find field "batchSizeOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
41 |      deriving ( ToSchema   GraknSchema "Options" )
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/src/Proto.hs:41:17: error:
    • Could not find field "explainOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
41 |      deriving ( ToSchema   GraknSchema "Options" )
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/src/Proto.hs:41:17: error:
    • Could not find field "inferOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
41 |      deriving ( ToSchema   GraknSchema "Options" )
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

@serras
Copy link
Collaborator

serras commented Nov 13, 2020

You need to use the same mapping in both. ToSchema and FromSchema take care of applying it in the right direction.

@haskie-lambda
Copy link
Author

haskie-lambda commented Nov 13, 2020

ok so using the handling acc. to the docs

type InferOptMapping = '[ "InferOpt" ':-> "inferOpt"
                        , "ExplainOpt" ':-> "explainOpt"
                        , "BatchSizeOpt" ':-> "batchSizeOpt" ]

data Options
  = InferOpt { infer :: [Bool] }
  | ExplainOpt { explain :: [Bool] }
  | BatchSizeOpt { batchSize :: [Int32] }
     deriving ( Generic )
     deriving ( ToSchema   GraknSchema "Options" 
              , FromSchema GraknSchema "Options" )
        via ( CustomFieldMapping "Options" InferOptMapping Options)

still results in

/home/faebl/programming/grakn_haskell/client_server_setup/server/src/Proto.hs:38:17: error:
    • Could not find field "BatchSizeOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
38 |      deriving ( ToSchema   GraknSchema "Options" 
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/home/faebl/programming/grakn_haskell/client_server_setup/server/src/Proto.hs:38:17: error:
    • Could not find field "ExplainOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
38 |      deriving ( ToSchema   GraknSchema "Options" 
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/home/faebl/programming/grakn_haskell/client_server_setup/server/src/Proto.hs:38:17: error:
    • Could not find field "InferOpt"
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
38 |      deriving ( ToSchema   GraknSchema "Options" 
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

could that be connected to Protocol Buffers Field Identifiers that are not yet present in the code?

if so, how would i specify them for this oneof schema?

@serras
Copy link
Collaborator

serras commented Nov 13, 2020

I am quite surprised by the original definition, to be honest. Those oneof blocks contain only one element each, whereas according to the spec they should form a single block. Maybe your original comment was right and all you need is a custom mapping and a record?

@haskie-lambda
Copy link
Author

I am confused with the semantics of this notation too;
however, this is a real life example from the grakn graph db grpc protocol

so just something along the lines of this?

data Options =
     Options { inferOpt :: [Bool]
             , explainOpt :: [Bool]
             , batchSizeOpt :: [Int32] }
     deriving ( Generic
              , ToSchema   GraknSchema "Options"
              , FromSchema GraknSchema "Options" )

how would this mapping roughly look like?

without a mapping the build error looks like this (if that helps)

/src/Proto.hs:47:17: error:
    • No instance for (Mu.Schema.Class.GToSchemaFieldTypeWrap
                         '[ 'DRecord
                              "Options"
                              '[ 'FieldDef "inferOpt" ('TUnion '[ 'TPrimitive Bool]),
                                 'FieldDef "explainOpt" ('TUnion '[ 'TPrimitive Bool]),
                                 'FieldDef "batchSizeOpt" ('TUnion '[ 'TPrimitive Int32])],
                            'DEnum "Type" '[ 'ChoiceDef "DATA", 'ChoiceDef "SCHEMA"],
                            'DRecord
                              "SessionOpenReq"
                              '[ 'FieldDef "database" ('TPrimitive T.Text),
                                 'FieldDef "type_" ('TOption ('TSchematic "Type")),
                                 'FieldDef "options" ('TOption ('TSchematic "Options"))],
                            'DRecord
                              "SessionOpenRes"
                              '[ 'FieldDef "sessionID" ('TPrimitive BS.ByteString)]]
                         ('TPrimitive Int32)
                         (C1 ('MetaCons "[]" 'PrefixI 'False) U1
                          :+: C1
                                ('MetaCons ":" ('InfixI 'LeftAssociative 9) 'False)
                                (S1
                                   ('MetaSel
                                      'Nothing
                                      'NoSourceUnpackedness
                                      'NoSourceStrictness
                                      'DecidedLazy)
                                   (Rec0 Int32)
                                 :*: S1
                                       ('MetaSel
                                          'Nothing
                                          'NoSourceUnpackedness
                                          'NoSourceStrictness
                                          'DecidedLazy)
                                       (Rec0 [Int32]))))
        arising from the 'deriving' clause of a data type declaration
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (ToSchema
                                        GraknSchema "Options" Options)
   |
47 |      deriving ( ToSchema   GraknSchema "Options"
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

@serras
Copy link
Collaborator

serras commented Nov 13, 2020

My gut feeling is that you are in the right direction, but there might be some missing instances for the case in which the union has a single element. Let me investigate a bit more, I'll try to report back soon.

@serras
Copy link
Collaborator

serras commented Nov 13, 2020

Ok, there are some issues I found. First of all, the types should not be wrapped in [], since they are not lists in the original schema:

data Options =
     Options { inferOpt :: Bool
             , explainOpt :: Bool
             , batchSizeOpt :: Int32 }
     deriving ( Generic
              , ToSchema   GraknSchema "Options"
              , FromSchema GraknSchema "Options" )

If that does not work, try making each option into its own data type.

data Options =
     Options { inferOpt :: InferOpt
             , explainOpt :: ExplainOpt
             , batchSizeOpt :: BatchSizeOpt }
     deriving ( Generic
              , ToSchema   GraknSchema "Options"
              , FromSchema GraknSchema "Options" )

data InferOpt = InferOpt Bool deriving (Generic)
data ExplainOpt = ExplainOpt Bool deriving (Generic)
data BatchSizeOpt = BatchSizeOpt Int32 deriving (Generic)

@haskie-lambda
Copy link
Author

haskie-lambda commented Nov 13, 2020

the second one did the trick thanks;

However, I needed to declare

instance Generic Int32 where

because GHC told me there is no known instance;
i guess this should be fine?

if i may:
there is one other thing that I am struggling with,
namely nested message definitions

as part of the same protocol, session grpc calls are defined like this:

message Session {

    enum Type {
        DATA = 0;
        SCHEMA = 1;
    }

    message Open {
        message Req {
            string database = 1;
            Type type = 2;
            Options options = 3;
        }
        message Res {
            bytes session_id = 1;
        }
    }

    message Close {
        message Req {
            bytes session_id = 1;
        }
        message Res {}
    }
}

How should those nested messages be converted to a data type?
Using just Res or Req does not work as there are multiple of these defined;
however, i also can't use someting along ToSchema Schema "Session.Open.Req" as that would need a type Session.Open.Req which is not valid in haskell;
using a record type, I don't know how to name its' constructors;
the nested messages seem like some kind of subtyping that I don't know how to do in haskell (except for using typeclasses but that seems weird here);

i tried just concattenting the message names like `SessionOpenReq" but that obviously didn't work out.

@serras
Copy link
Collaborator

serras commented Nov 13, 2020

About Generic Int32, I think that is not the right solution. Instead, we should add Int32 to the list of "primitive types" in Mu-Haskell, in the same way we treat Bool or Char.

About the second problem, I am looking again at the spec to see what that means. I think we do some kind of normalization in which every message goes as a top-level one (right, @kutyel?). But I am not sure how it works when you have several messages with the same name. Could you try to define a single data type for it, and post the error?

@haskie-lambda
Copy link
Author

haskie-lambda commented Nov 13, 2020

sure;
to submit an example as complete as possible:

.
├── protobuf
│   ├── grakn.proto
│   ├── options.proto
│   ├── session.proto
├── src
│   ├── Main.hs
│   ├── Proto.hs
│   └── Server.hs
[cabal files and stuff]

grakn.proto:

// Copyright (C) 2020 Grakn Labs
// GNU Affero GPL notice

syntax = "proto3";

option java_package = "grakn.protocol";
option java_outer_classname = "GraknProto";

import "./database.proto";
import "./session.proto";
import "./transaction.proto";

package grakn.protocol;

service Grakn {
    rpc session_open (SessionOpenReq) returns (SessionOpenRes);
}

session.proto

// Copyright (C) 2020 Grakn Labs
// GNU Affero GPL notice

syntax = "proto3";

option java_package = "grakn.protocol";
option java_outer_classname = "SessionProto";

import "protobuf/options.proto";

package grakn.protocol;

message Session {

    enum Type {
        DATA = 0;
        SCHEMA = 1;
    }

    message Open {
        message Req {
            string database = 1;
            Type type = 2;
            Options options = 3;
        }
        message Res {
            bytes sessionID = 1;
        }
    }
/*
    message Close {
        message Req {
            bytes sessionID = 1;
        }
        message Res {}
    }
*/
}

options.proto

// Copyright (C) 2020 Grakn Labs
// GNU Affero GPL notice
syntax = "proto3";

option java_package = "grakn.protocol";
option java_outer_classname = "OptionsProto";

package grakn.protocol;

// TODO: Replace 'oneof' with 'optional' when upgraded to Protobuf 3.13 everywhere
// https://github.com/protocolbuffers/protobuf/issues/1606#issuecomment-618687169

message Options {
    oneof inferOpt {
        bool infer = 1;
    }
    oneof explainOpt {
        bool explain = 2;
    }
    oneof batchSizeOpt {
        int32 batchSize = 3;
    }
}

Proto.hs

{-# language CPP                   #-}
{-# language DataKinds             #-}
{-# language DeriveAnyClass        #-}
{-# language DeriveGeneric         #-}
{-# language DerivingStrategies    #-}
{-# language DerivingVia           #-}
{-# language DuplicateRecordFields #-}
{-# language FlexibleContexts      #-}
{-# language FlexibleInstances     #-}
{-# language MultiParamTypeClasses #-}
{-# language PolyKinds             #-}
{-# language TemplateHaskell       #-}
{-# language TypeFamilies          #-}
{-# language TypeOperators         #-}
module Proto where

import GHC.Generics
import GHC.Int
import Data.Void
import           Mu.Quasi.GRpc
import Mu.Quasi.ProtoBuf
import Mu.Schema
import qualified Data.Text as T
import qualified Data.ByteString as BS
import Mu.Adapter.ProtoBuf

grpc "GraknOptionsSchema" (++ "Service") "protobuf/options.proto"
grpc "GraknSessionSchema" (++ "Service") "protobuf/session.proto"
grpc "GraknSchema" (++ "Service") "protobuf/grakn.proto"

instance Generic Int32 where

data Options =
     Options { inferOpt :: InferOpt
             , explainOpt :: ExplainOpt
             , batchSizeOpt :: BatchSizeOpt }
     deriving ( Generic
              , ToSchema   GraknOptionsSchema "Options"
              , FromSchema GraknOptionsSchema "Options" )

data InferOpt = InferOpt Bool deriving (Generic)
data ExplainOpt = ExplainOpt Bool deriving (Generic)
data BatchSizeOpt = BatchSizeOpt Int32 deriving (Generic)

data GType = DATA | SCHEMA
     deriving ( Generic
              , ToSchema   GraknSessionSchema "Type"
              , FromSchema GraknSessionSchema "Type" )

type TypeFieldMapping = '[ "type_" ':-> "type" ]

{- works until here -}
-- this was just a guess on how it could look like
data SessionOpenReq =
     SessionOpenReq { database :: T.Text
                    , type_     :: GType 
                    , options  :: Options }
     deriving ( Generic )
     deriving ( ToSchema   GraknSessionSchema "SessionOpenReq" 
              , FromSchema GraknSessionSchema "SessionOpenReq" )
        via (CustomFieldMapping "SessionOpenReq" TypeFieldMapping SessionOpenReq)

build results in

[1 of 3] Compiling Proto

/src/Proto.hs:58:17: error:
    • Cannot find type "SessionOpenReq" in the schema
    • When deriving the instance for (ToSchema
                                        GraknSessionSchema "SessionOpenReq" SessionOpenReq)
   |
58 |      deriving ( ToSchema   GraknSessionSchema "SessionOpenReq" 
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/src/Proto.hs:59:17: error:
    • Cannot find type "SessionOpenReq" in the schema
    • When deriving the instance for (FromSchema
                                        GraknSessionSchema "SessionOpenReq" SessionOpenReq)
   |
59 |               , FromSchema GraknSessionSchema "SessionOpenReq" )
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

using Req instead of SessionOpenReq:

[1 of 3] Compiling Proto

/src/Proto.hs:58:17: error:
    • No instance for (Mu.Schema.Class.GToSchemaFieldType
                         '[ 'DRecord "Session" '[],
                            'DEnum "Type" '[ 'ChoiceDef "DATA", 'ChoiceDef "SCHEMA"],
                            'DRecord "Open" '[],
                            'DRecord
                              "Req"
                              '[ 'FieldDef "database" ('TPrimitive T.Text),
                                 'FieldDef "type" ('TOption ('TSchematic "Type")),
                                 'FieldDef "options" ('TOption ('TSchematic "Options"))],
                            'DRecord
                              "Res" '[ 'FieldDef "sessionID" ('TPrimitive BS.ByteString)]]
                         ('TOption ('TSchematic "Options"))
                         Options)
        arising from the 'deriving' clause of a data type declaration
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (ToSchema
                                        GraknSessionSchema "Req" Req)
   |
58 |      deriving ( ToSchema   GraknSessionSchema "Req" 
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/src/Proto.hs:59:17: error:
    • No instance for (Mu.Schema.Class.GFromSchemaFieldType
                         '[ 'DRecord "Session" '[],
                            'DEnum "Type" '[ 'ChoiceDef "DATA", 'ChoiceDef "SCHEMA"],
                            'DRecord "Open" '[],
                            'DRecord
                              "Req"
                              '[ 'FieldDef "database" ('TPrimitive T.Text),
                                 'FieldDef "type" ('TOption ('TSchematic "Type")),
                                 'FieldDef "options" ('TOption ('TSchematic "Options"))],
                            'DRecord
                              "Res" '[ 'FieldDef "sessionID" ('TPrimitive BS.ByteString)]]
                         ('TOption ('TSchematic "Options"))
                         Options)
        arising from the 'deriving' clause of a data type declaration
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (FromSchema
                                        GraknSessionSchema "Req" Req)
   |
59 |               , FromSchema GraknSessionSchema "Req" )
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

grakn protocol is defined here

@kutyel
Copy link
Member

kutyel commented Nov 13, 2020

About Generic Int32, I think that is not the right solution. Instead, we should add Int32 to the list of "primitive types" in Mu-Haskell, in the same way we treat Bool or Char.

About the second problem, I am looking again at the spec to see what that means. I think we do some kind of normalization in which every message goes as a top-level one (right, @kutyel?). But I am not sure how it works when you have several messages with the same name. Could you try to define a single data type for it, and post the error?

I think in avro we do unfold the nested messages to the top level yes, I'm unfamiliar with the protobuff part 😕

@serras
Copy link
Collaborator

serras commented Nov 17, 2020

@faeblDevelopment Does #260 fix the problem for you? Now you should be able to refer to the nested types as Outer.Inner, following the usual Protocol Buffers conventions.

@haskie-lambda
Copy link
Author

It definately looks as if it would solve the problem;

However, how do I test this?
i tried changing my stack.yaml to

extra-deps:
# mu
#- mu-schema-0.3.1.1
#- mu-rpc-0.4.0.0
#- mu-optics-0.3.0.0
#- mu-avro-0.4.0.1
#- mu-protobuf-0.4.0.1
#- mu-grpc-server-0.4.0.0
#- mu-grpc-client-0.4.0.0
#- mu-grpc-common-0.4.0.0
- git: https://github.com/higherkindness/mu-haskell.git
  commit: 0a7441ef5c75df34333f364241dc82b66fe38030
# dependencies of mu
- http2-client-0.9.0.0
- http2-grpc-types-0.5.0.0
- http2-grpc-proto3-wire-0.1.0.0
- warp-grpc-0.4.0.1
- proto3-wire-1.2.0
- parameterized-0.5.0.0
- compendium-client-0.2.0.0@sha256:df13fb2814ab9a3bb57cb0ac34a28d3a0915c66c4eadc694552f7fe364d20988,1035
- http2-client-grpc-0.8.0.0@sha256:5bea1edf5fa3572155f55c5b38cfab7063cbde613702898986c26d70bc9f3f9e,1861

but it complains there is no .cabal file
i am not familiar with the repository layout and therefore a bit confused how to convince stack to use the HEAD version of mu.
i saw that you use nix for building the package;
is there an easy way to tell stack to to build the downloaded package using nix?

@serras
Copy link
Collaborator

serras commented Nov 17, 2020

You need to write the following in your extra-deps:

- git: https://github.com/higherkindness/mu-haskell.git
  commit: 0a7441ef5c75df34333f364241dc82b66fe38030
  subdirs:
    - core/schema
    - core/rpc
    - core/optics
    - adapter/avro
    - adapter/protobuf
    - grpc/common
    - grpc/client
    - grpc/server

@haskie-lambda
Copy link
Author

haskie-lambda commented Nov 17, 2020

thanks;

i tried it with the Session.Open.Res and that works perfectly as expected;
Using Session.Open.Req makes some problem.
this works fine:

data SessionOpenRes =
     SessionOpenRes { sessionID :: BS.ByteString }
     deriving ( Generic
              , ToSchema   GraknSessionSchema "Session.Open.Res"
              , FromSchema GraknSessionSchema "Session.Open.Res" )

this however:

type TypeFieldMapping = '[ "type_" ':-> "type" ]

data SessionOpenReq =
     SessionOpenReq { database  :: T.Text
                    , type_     :: GType 
                    , options   :: Options }
     deriving ( Generic )
     deriving ( ToSchema   GraknSessionSchema "Session.Open.Req" 
              , FromSchema GraknSessionSchema "Session.Open.Req" )
        via (CustomFieldMapping "Session.Open.Req" TypeFieldMapping SessionOpenReq)

generates

/src/Proto.hs:59:17: error:
    • No instance for (Mu.Schema.Class.GToSchemaFieldType
                         '[ 'DRecord "Session" '[],
                            'DEnum "Session.Type" '[ 'ChoiceDef "DATA", 'ChoiceDef "SCHEMA"],
                            'DRecord "Session.Open" '[],
                            'DRecord
                              "Session.Open.Req"
                              '[ 'FieldDef "database" ('TPrimitive T.Text),
                                 'FieldDef "type" ('TOption ('TSchematic "Session.Type")),
                                 'FieldDef "options" ('TOption ('TSchematic "Options"))],
                            'DRecord
                              "Session.Open.Res"
                              '[ 'FieldDef "sessionID" ('TPrimitive BS.ByteString)]]
                         ('TOption ('TSchematic "Options"))
                         Options)
        arising from the 'deriving' clause of a data type declaration
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (ToSchema
                                        GraknSessionSchema "Session.Open.Req" SessionOpenReq)
   |
59 |      deriving ( ToSchema   GraknSessionSchema "Session.Open.Req" 
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/src/Proto.hs:60:17: error:
    • No instance for (Mu.Schema.Class.GFromSchemaFieldType
                         '[ 'DRecord "Session" '[],
                            'DEnum "Session.Type" '[ 'ChoiceDef "DATA", 'ChoiceDef "SCHEMA"],
                            'DRecord "Session.Open" '[],
                            'DRecord
                              "Session.Open.Req"
                              '[ 'FieldDef "database" ('TPrimitive T.Text),
                                 'FieldDef "type" ('TOption ('TSchematic "Session.Type")),
                                 'FieldDef "options" ('TOption ('TSchematic "Options"))],
                            'DRecord
                              "Session.Open.Res"
                              '[ 'FieldDef "sessionID" ('TPrimitive BS.ByteString)]]
                         ('TOption ('TSchematic "Options"))
                         Options)
        arising from the 'deriving' clause of a data type declaration
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (FromSchema
                                        GraknSessionSchema "Session.Open.Req" SessionOpenReq)
   |
60 |               , FromSchema GraknSessionSchema "Session.Open.Req" )
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

i verified that it is not the type mapping;
when changing that the error becomes that type cannot be found as expected;

could it be connected with GType being defined in a different schema?

@serras
Copy link
Collaborator

serras commented Nov 17, 2020

Unfortunately, as of now you need to have the entire schema in one single file. #257 is related to this.

@haskie-lambda
Copy link
Author

I added the Options Message to the session.proto file and adjusted the code accordingly;

however, the error message is still the same (just that the Options message Mu.Schema.Class.GFromSchemaFieldType is now also visible)
so

grpc "GraknSessionSchema" (++ "Service") "protobuf/session.proto"

instance Generic Int32 where

data Options =
     Options { inferOpt :: InferOpt
             , explainOpt :: ExplainOpt
             , batchSizeOpt :: BatchSizeOpt }
     deriving ( Generic
              , ToSchema   GraknSessionSchema "Options"
              , FromSchema GraknSessionSchema "Options" )

data InferOpt = InferOpt Bool deriving (Generic)
data ExplainOpt = ExplainOpt Bool deriving (Generic)
data BatchSizeOpt = BatchSizeOpt Int32 deriving (Generic)

data GType = DATA | SCHEMA
     deriving ( Generic
              , ToSchema   GraknSessionSchema "Session.Type"
              , FromSchema GraknSessionSchema "Session.Type" )

    
type TypeFieldMapping = '[ "type_" ':-> "type" ]

data SessionOpenReq =
     SessionOpenReq { database  :: T.Text
                    , type_     :: GType 
                    , options   :: Options }
     deriving ( Generic )
     deriving ( ToSchema   GraknSessionSchema "Session.Open.Req" 
              , FromSchema GraknSessionSchema "Session.Open.Req" )
        via (CustomFieldMapping "Session.Open.Req" TypeFieldMapping SessionOpenReq)

results in

/src/Proto.hs:59:17: error:
    • No instance for (Mu.Schema.Class.GToSchemaFieldType
                         '[ 'DRecord
                              "Options"
                              '[ 'FieldDef "inferOpt" ('TUnion '[ 'TPrimitive Bool]),
                                 'FieldDef "explainOpt" ('TUnion '[ 'TPrimitive Bool]),
                                 'FieldDef "batchSizeOpt" ('TUnion '[ 'TPrimitive Int32])],
                            'DRecord "Session" '[],
                            'DEnum "Session.Type" '[ 'ChoiceDef "DATA", 'ChoiceDef "SCHEMA"],
                            'DRecord "Session.Open" '[],
                            'DRecord
                              "Session.Open.Req"
                              '[ 'FieldDef "database" ('TPrimitive T.Text),
                                 'FieldDef "type" ('TOption ('TSchematic "Session.Type")),
                                 'FieldDef "options" ('TOption ('TSchematic "Options"))],
                            'DRecord
                              "Session.Open.Res"
                              '[ 'FieldDef "sessionID" ('TPrimitive BS.ByteString)]]
                         ('TOption ('TSchematic "Options"))
                         Options)
        arising from the 'deriving' clause of a data type declaration
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (ToSchema
                                        GraknSessionSchema "Session.Open.Req" SessionOpenReq)
   |
59 |      deriving ( ToSchema   GraknSessionSchema "Session.Open.Req" 
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

@serras
Copy link
Collaborator

serras commented Nov 17, 2020

Interesting. Could you send over the entire project so I can inspect the current state?

@haskie-lambda
Copy link
Author

sure: here is a link to the repo

@serras
Copy link
Collaborator

serras commented Nov 19, 2020

The solution is to change SessionOpenReq to:

data SessionOpenReq =
     SessionOpenReq { database  :: T.Text
                    , type_     :: Maybe GType 
                    , options   :: Maybe Options }

This (really tiny) bit of information in the docs point out the reason:

Protocol Buffers version 3 has complicated rules about when a field may or may not appear in the message [..] for references to other message we can spot the difference, so those references must be wrapped in a Maybe.

The general rule when translating Protocol Buffers to Mu-Haskell is that anything which is not a primitive type should appear wrapped in Maybe, since the protocol has no notion of "required" field.

@haskie-lambda
Copy link
Author

Thanks very much, I must have skipped over this part of the documentation too fast.

great to see that example work 👍
armed with all that info i think i can now make the rest of the API and build the library that I wanted to make with mu :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested rpc
Projects
None yet
Development

No branches or pull requests

3 participants