The example from docs
type
BinaryTree*[T] = ref object # BinaryTree is a generic type with
# generic param ``T``
le, ri: BinaryTree[T] # left and right subtrees; may be nil
data: T # the data stored in a node
proc newNode*[T](data: T): BinaryTree[T] =
# constructor for a node
new(result)
result.data = data
Is it possible to use something like one-line shortcut like
proc newNode*[T](data: T): BinaryTree[T] =
data.new(data = data)
The tutorial says: "Note that referential data types will be nil at the start of the procedure, and thus may require manual initialisation" and here "To allocate a new traced object, the built-in procedure new must be used". But if you really need to save that line, you can make a template:
template aNewNode(data: untyped): void =
new(result)
result.data = data
proc newNode*[T](data: T): BinaryTree[T] =
# constructor for a node
aNewNode data
Related
I am not sure if this is currently possible (and maybe it is not even advisable), but I would like to be able to catch the output of a compiler error and reuse it in code. An example would be:
type IntOrString = int | string
var s: seq[IntOrString]
this code would not compile with error:
/usercode/in.nim(2, 5) Error: invalid type: 'IntOrString' in this context: 'seq[IntOrString]' for var
I am interested in a way to be able to use the message of this error in the code.
My use case is being able to easily document and discuss compiler errors in nimib. If I were to write a document that shows and discuss the different type of compiler errors catching the message automatically that could be useful (a workaround right now would be to write the code to file and compile with verbosity 0).
It is possible to use the compiler api to catch errors:
import ../compiler/[nimeval, llstream, ast, lineinfos, options], os, strformat
let std = findNimStdLibCompileTime()
let modules = [std, std / "pure", std / "std", std / "core"]
var intr = createInterpreter("script", modules)
intr.registerErrorHook proc(config:ConfigRef,info:TLineInfo,msg:string,severity:Severity) =
raise newException(CatchableError,&"{severity}: {(info.line,info.col)} {msg}")
)
try:
intr.evalScript(llStreamOpen("""type IntOrString = int | string
var s: seq[IntOrString]"""))
except CatchableError as e:
echo e.msg
echo "done"
destroyInterpreter(intr)
outputs:
Error: (2, 4) invalid type: 'IntOrString' in this context: 'seq[IntOrS
tring]' for var
done
Caveat: you can't run runtime code at compile time, for example, trying to run
type Dog = object of RootObj
method speak(i:Dog):string = "woof"
echo Dog().speak()
in the interpreter will give you errors, instead you would have to do something like:
type Dog = object of RootObj
method speak*(i:Dog):string = "woof"
proc newDog*():Dog = discard
let
dog = intr.callRoutine(intr.selectRoutine("newDog"),[])
speech = intr.callRoutine(intr.selectRoutine("speak"),[dog])
if speech.kind == nkStrLit:
echo speech.strval
I've only just started with Nim, hence it possibly is a simple question. We need to do many lookups into data that are stored in a file. Some of these files are too large to load into memory, hence the mmapped approach. I'm able to mmap the file by means of memfiles and either have a pointer or MemSlice at my hand. The file and the memory region are read-only, and hence have a fixed size. I was hoping that I'm able to access the data as immutable fixed size byte and char arrays without copying them, leveraging all the existing functionalities available to seqs, arrays, strings etc.. All the MemSlice / string methods copy the data, which is fair, but not what I want (and in my use case don't need).
I understand array, strings etc. types have a pointer to the data and a len field. But couldn't find a way to create them with a pointer and len. I assume it has something to do with ownership and refs to mem that may outlive my slice.
let mm = memfiles.open(...)
let myImmutableFixesSizeArr = ?? # cast[ptr array[fsize, char]](mm.mem) doesn't compile as fsize needs to be const. Neither could I find something like let x: [char] = array_from(mm.mem, fsize)
let myImmutableFixedSizeString = mm[20, 30].to_fixed_size_immutable_string # Create something that is string like so that I can use all the existing string methods.
UPDATE: I did find https://forum.nim-lang.org/t/4680#29226 which explains how to use OpenArray, but OpenArray is only allowed as function argument, and you - if I'm not mistaken - it is doesn't behave like a normal array.
Thanks for your help
It is not possible to convert a raw char array in memory (ptr UncheckedArray[char]) to a string without copying, only to an openArray[char] (or cstring)
So it won't be possible to use procs that expect a string, only those that accept openArray[T] or openArray[char]
Happily an openArray[T] behaves exactly like a seq[T] when sent to a proc.
({.experimental:"views".} does let you assign an openArray[T] to a local variable, but it's not anywhere near ready for production)
you can use the memSlices iterator to loop over delimited chunks in a memFile without copying:
import memfiles
template toOpenArray(ms: MemSlice, T: typedesc = byte): openArray[T] =
##template because openArray isn't a valid return type yet
toOpenArray(cast[ptr UncheckedArray[T]](ms.data),0,(ms.size div sizeof(T))-1)
func process(slice:openArray[char]) =
## your code here but e.g.
## count number of A's
var nA: int
for ch in slice.items:
if ch == 'A': inc nA
debugEcho nA
let mm = memfiles.open("file.txt")
for slice in mm.memSlices:
process slice.toOpenArray(char)
Or, to work with some char array represented in the middle of the file, you can use pointer arithmetic.
import memfiles
template extractImpl(typ,pntr,offset) =
cast[typ](cast[ByteAddress](pntr)+offset)
template checkFileLen(memfile,len,offset) =
if offset + len > memfile.size:
raise newException(IndexDefect,"file too short")
func extract*(mm: MemFile,T:typedesc, offset:Natural): ptr T =
checkFileLen(mm,T,offset)
result = extractImpl(ptr T,mm.mem,offset)
func extract*[U](mm: MemFile,T: typedesc[ptr U], offset: Natural): T =
extractImpl(T,mm.mem,offset)
let mm = memfiles.open("file.txt")
#to extract a compile-time known length string:
let mystring_offset = 3
const mystring_len = 10
type MyStringT = array[mystring_len,char]
let myString:ptr MyStringT = mm.extract(MyStringT,mystring_offset)
process myString[]
#to extract a dynamic length string:
let size_offset = 14
let string_offset = 18
let sz:ptr int32 = mm.extract(int32,size_offset)
let str:ptr UncheckedArray[char] = mm.extract(ptr UncheckedArray[char], string_offset)
checkFileLen(mm,sz[],string_offset)
process str.toOpenArray(0,sz[]-1)
How to handle a case when proc returns void as generic return type T?
Example - when reading a file we have return value - the file content, but when writing a file there's no return value.
playground
import sugar
proc open_file[T](path: string, cb: (proc (file: string): T)): T =
cb("file descriptor")
proc read_file(path: string): string =
open_file(path, (file) => "file content")
proc write_file(path, content: string): void =
discard open_file(path, proc (file: auto): bool =
echo "writing content to file"
false
)
echo read_file("/some-path")
write_file("/some-path", "some content")
Is there a better way than a workaround in this example - when we returning something (false) and discarding it?
You're making a big misunderstanding here - the discard that you use is related to the return value in open_file, it's not related to the void in your write_file at all.
void as a return type is superficial (doesn't need to be specified when there's no return type) and it literally means "return nothing" (it's not like C's void), but Nim requires you to handle return values from any procedures unless they are marked as {.discardable.}
So in your code example, there are no workarounds at all, it's normal Nim code (except for the fact that void is not required to be written) - see https://nim-lang.org/docs/manual.html#statements-and-expressions-discard-statement
i was wondering if it's possible to generate a something similar to a simple type provider (Record or Union, with no members, just the matching name with the string name) from a string,
at compile time.
mixing this
http://www.readcopyupdate.com/blog/2014/09/18/faking-typeclasses-using-static-type-constraints.html
and this
Create Discriminated Union Case from String
or a similar approach to record types.
what i would like to obtain in, for example (not necessarily with the same approach):
[<Literal>]
let myTypeName = "One"
type SingleStringTypeProvider = ... (here implementation)
type Provided = SingleStringTypeProvider<singleString>
let typeName = typeof<Provided.One>.Name
final result:
One is a Type at compile time (and not a method nor a function)
EDIT
As suggested in the first answer (thanks a lot : )), I have tried to implement it with type provided, but i am still struggling when trying to access the type provider from my script file, apparently i see only the created type but non the type provider itself?
module SimpleStringProvider
open ProviderImplementation.ProvidedTypes
open Microsoft.FSharp.Core.CompilerServices
[<TypeProvider>]
type SingleStringTypeProvider (config : TypeProviderConfig) as this =
inherit TypeProviderForNamespaces (config)
let asm = System.Reflection.Assembly.GetExecutingAssembly()
let ns = "SimpleStringProvider"
let stringProvider = ProvidedTypeDefinition(asm, ns, "SingleStringTypeProvider", Some(typeof<obj>))
// Define one static parameter with type name
let parameter = ProvidedStaticParameter("TypeName", typeof<string>)
do stringProvider.DefineStaticParameters([parameter], fun typeName args ->
// Create the main type (this corresponds to `Provided`)
let resTy = ProvidedTypeDefinition(asm, ns, typeName, Some(typeof<obj>))
// Add a nested type as a member using the name from the parameter
let typeName = args.[0] :?> string
ProvidedTypeDefinition(typeName, None)
|> resTy.AddMember
resTy )
[<assembly:TypeProviderAssembly>]
do ()
and here is the code in my script.fsx file, probably i am making some silly mistakes i guess.
#r #".\testType\SimpleStringProvider.dll"
open SimpleStringProvider
type x = SimpleStringProvider.SingleStringTypeProvider<"test">
ERROR in script.fsx file
The non-generic type 'SimpleStringProvider.SingleStringTypeProvider'
does not expect any type arguments, but here is given 1 type
argument(s)
You can provide nested types and those can be based on the static parameter. In your example Provided is a type and Provided.One can be a nested type.
To do this, you can write something like this:
[<TypeProvider>]
type public SingleStringTypeProvider(cfg:TypeProviderConfig) as this =
inherit TypeProviderForNamespaces()
// Generate namespace and the main type provider
let asm = System.Reflection.Assembly.GetExecutingAssembly()
let ns = "Samples"
let stringProvider = ProvidedTypeDefinition(asm, ns, "SingleStringTypeProvider", Some(typeof<obj>))
// Define one static parameter with type name
let parameter = ProvidedStaticParameter("TypeName", typeof<string>)
do stringProvider.DefineStaticParameters([parameter], fun typeName args ->
// Create the main type (this corresponds to `Provided`)
let resTy = ProvidedTypeDefinition(asm, ns, typeName, Some typeof<IniFile>)
// Add a nested type as a member using the name from the parameter
let typeName = args.[0] :?> string
ProvidedTypeDefinition(typeName, None)
|> resTy.AddMember
resTy )
[<assembly:TypeProviderAssembly>]
do()
I have not tested this, so you might need to do some tweaking, but I think it should work.
I've spent some time looking for some alternative to handle generic objects, I've seen questions similar to mine, but not as specific I suppose?
Protocol buffers has multiple scalar types that I can use, however they are mostly primitive.
I want my message to be flexible and be able to have a field that is a List of some sort.
Let's say my .proto file looked like this:
message SomeMessage
{
string datetime = 1;
message inputData // This would be a list
{
repeated Object object = 1;
}
message Object
{
? // this need to be of a generic type - This is my question
// My work around - Using extentions with some Object
//List all primitive scalar types as optional and create an extension 100 to max;
}
message someObject //some random entity - for example, employee/company etc.
{
optional string name = 1; optional int32 id = 2;
}
extend Object
{
optional someObject obj = 101;
}
}
And this would be fine, and would work, and I'd have a List where Objects could be of any primitive type or could be List < someObject >.
However- The problem here, is that any time I needed to handle a new type of object, I'd need to edit my .proto file, recompile for C# and java (The languages I need it for)...
If protocol buffers is not able to handle generic object types, is there another alternative that can?
Any help on this matter is greatly appreciated.
As Marc Gravell stated above - Protocol Buffers do not handle generics or inheritance.
Though I am late, just for the sake of new audience,
you can use bytes in place of object and that can be any object which you can serialize/de-serialize.
It IS possible to achieve generic message functionality but still adding new types will require rebuilding proto classes.
You use wrapper class
message Wrapper {
extensions 1000 to max;
required uint32 type = 1;
}
Then add some types
message Foo {
extend Wrapper {
optional Foo item = 1000;
}
optional int attr1_of_foo = 1;
optional int attr2_of_foo = 2;
optional int attr3_of_foo = 3;
}
message Bar {
extend Wrapper {
optional Bar item = 1001;
}
optional int attr1_of_bar = 1;
optional int attr2_of_bar = 2;
optional int attr3_of_bar = 3;
}
See how we extending Wrapper class in classes that we want to be stored by Wrapper class using extension.
Now, example of creating Foo wrapped object. I'm using Python, since it's most condensed form. Other languages can do the same.
wrapper = Wrapper()
wrapper.type = Foo.ITEM_FIELD_NUMBER
foo = wrapper.Extensions[Foo.item]
foo.attr1_of_foo = 1
foo.attr2_of_foo = 2
foo.attr3_of_foo = 3
data = wrapper.SerializeToString()
And example of deserializing
wrapper = Wrapper()
wrapper.ParseFromString(data)
if wrapper.type == Foo.ITEM_FIELD_NUMBER:
foo = wrapper.Extensions[Foo.item]
elif wrapper.type == Bar.ITEM_FIELD_NUMBER:
bar = wrapper.Extensions[Bar.item]
else:
raise Exception('Unrecognized wrapped type: %s' % wrapper.type)
Now, because you want generic collection, make Wrapper a repeated field of other message and voilĂ .
Of course it's not complete solution, this architecture will need some more packaging to make it easy to use. For more information read about Protobuf extensions, especially nested ones (https://developers.google.com/protocol-buffers/docs/proto#nested) or google about item marshalling.
Here is the protobuf 3 definition of Struct which basically uses oneof to define such "generic" message type.