Skip to content

Instantly share code, notes, and snippets.

@RobertAKARobin
Last active August 20, 2024 17:25
Show Gist options
  • Save RobertAKARobin/a1cba47d62c009a378121398cc5477ea to your computer and use it in GitHub Desktop.
Save RobertAKARobin/a1cba47d62c009a378121398cc5477ea to your computer and use it in GitHub Desktop.
Python Is Not A Great Programming Language

Python is not a great programming language.

It's great for beginners. Then it turns into a mess.

What's good

  • A huge ecosystem of good third-party libraries.
  • Named arguments.
  • Multiple inheritance.

What should be good

  • It's easy to learn and read. However, it's only easy to learn and read at the start. Once you get past "Hello world" Python can get really ugly and counterintuitive.
  • The Pythonic philosophy that "There should be one -- and preferably only one -- obvious way to do it." As someone who loves working within rules and rigid frameworks, I love this philosophy! As someone who writes Python, I really wish Python actually stuck to this philosophy. See below.

What's "meh"

  • Forced indentation. Some love it because it enforces consistency and a degree of readability. Some hate it because they think it enforces the wrong consistency. To each their own.
  • Dynamic typing. There are lots of dynamically-typed languages and lots of statically-typed languages. Which kind of typing is better isn't a Python debate, it's a general programming debate.

What's bad

  • 400 ways (more or less) to interpolate strings. This prints "Hello Robin!" 3 times:

    user = {'name': "Robin"}
    print(f"Hello {user['name']}!")
    print("Hello {name}!".format(**user))
    print("Hello %(name)s!" % user)
    

    If there was a unique and obvious use-case for each of these then that would be one thing, but there's not.

  • 69 top-level functions that you have to just memorize. GvR's explanation sounds nice, but in reality it makes things confusing.

  • map doesn't return a list, even though the whole point of a mapping function is to create one list from another. Instead it returns a map object, which is pretty much useless since it's missing append, reverse, etc. So, you always have to wrap it in list(), or use a list comprehension, which, speaking of...

  • List comprehensions are held up as an excellent recent-ish addition to Python. People say they're readable. That's true for simple examples (e.g. [x**2 for x in range(10)]) but horribly untrue for slightly more complex examples (e.g. [[row[i] for row in matrix] for i in range(4)]). I chalk this up to...

  • Weird ordering in ternary/one-line expressions. Most languages follow a consistent order where first you declare conditions, then you do stuff based the on those conditions:

    if user.isSignedIn then user.greet else error
    
    for user in signedInUsers do user.greet
    

    Python does this in the opposite order:

    user.greet if user.isSignedIn else error
    
    [user.greet for user in signedInUsers]
    

    This is fine for simple examples. It's bad for more complex logic because you have to first find the middle of the expression before you can really understand what you're reading.

  • Syntax for tuples. If you write a single-item tuple (tuple,) but forget the trailing comma, it's no longer a tuple but an expression. This is a really easy mistake to make. Considering the only difference between tuples and lists is mutability, it would make much more sense to use the same syntax [syntax] as lists, which does not require a trailing comma, and add a freeze or immutable method. Speaking of...

  • There's no way to make dicts or complex objects immutable.

  • Regular expressions require a lot of boilerplate:

    re.compile(r"regex", re.I | re.M)
    

    Compared to JavaScript or Ruby:

    /regex/ig
    
  • The goofy string literal syntaxes: f'', u'', b'', r''.

  • The many "magic" __double-underscore__ attributes that you just have to memorize.

  • You can't reliably catch all errors and their messages in one statement. Instead you have to use something like sys.exc_info()[0]. You shouldn't have a catch-all in production of course, but in development it's very useful, so this unintuitive extra step is annoying.

  • Dev environments. Setting up an environment is a problem in any langauge, but other languages have solved the problem better than Python. For example, while npm has its warts, it is widely accepted that a fresh environment should be set up with npm i && npm run [script]. Meanwhile each Python project seems to require a unique mish-mash of pip and pipenv and venv and other shell commands.

What's bad about the culture

Most programmers will acknowledge criticisms of their favorite language. Instead, Pythonists will say, "You just don't understand Python."

Most programmers will say a piece of code is bad if it's inefficient or hard to read. Pythonists will say a piece of code is bad if "it isn't Pythonic enough." This is about as helpful as someone saying your taste in music is bad because "it isn't cultured enough."

Pythonists have a bit of a superiority complex.

@muratyaman
Copy link

:D ironic to see quite a few comments like:

"you don't know python, maaan"

people are kidding themselves and only deceiving the young programmers about Python!

if a programming language makes me think unnecessarily and reduces my efficiency

(never mind the runtime efficiency! another big topic),

it is not a good one!

no one can defend this:

data = [None] * 100

here is a hard one for Python as it chokes! it is easy in other languages.

class Person:
  def __init__(self, name = '', age = 0, is_fan = False):
    self.name   = name
    self.age    = age
    self.is_fan = is_fan
  # end def

  # without this, method hydrate() does not work;
  # it causes "TypeError: 'Person' object does not support item assignment"
  def __setitem__(self, k, v):
    if k in self.__dict__.keys():
      self[k] = v # FIXME: this causes RecursionError 
    # end if
  # end def

  def __str__(self):
    return str(self.__dict__)
  # end def

  def info(self):
    not_str = '' if self.is_fan else 'not '
    return f'{self.name} is {self.age} years old, and is {not_str}a fan'
  # end def

  def hydrate(self, dictObj):
    for key, value in dictObj.items():
      self[key] = value # this requires magic method __setitem__()
    # end for
  # end def

# end class

p = Person('John Smith', 20, True)
print(p.info())
print(p)

data = {
  'name': 'Jane Smith',
  'age': 21,
  'is_fan': False,
}

print('copying data...')
p.hydrate(data)
print(p.info())

to avoid twitching, I had to use comments to highlight the ends of if/class/for/method blocks!!! ugh!

I know the sophisticated ones will tell me off and show me better ways of doing things

(in reality offering a work around for an absurd unintended side-effect of some features) and say:

learn some python, maaan!

happy coding, everyone!

...using the programming language that you feel comfortable and efficient; the one that makes you love your job, not hate!

@ekeyser
Copy link

ekeyser commented Oct 14, 2021

I believe the only reason Python became a thing was because it was an alternative to Perl. I personally do not think Python is all that great a language. The comments on here from Python advocates indicate that they cannot take criticism.

@ekeyser
Copy link

ekeyser commented Oct 15, 2021

Yeah, what could possibly be disjointed with multiplying a set with an integer ;-) Is that idiomatic? Most definitely since idiomatic means you only know something from extended use of the language itself. What exactly does it say it will do? [None], 0, I mean, if you're trying to figure it out by deduction what would you get from None * 100? Maybe it's a 100 element null set? I'm new to this thread and admittedly I don't code in Python a ton but I'm guessing from the context of the post that this is Python's way of initializing a 100 element null set (or some n-length set in general). There is no way this is "natural" and would agree with @muratyaman. The only way you would know this is exactly what you said - idiomatically. But idiomatic means you know it only from using the language not from inference/intuition/deduction. Why wouldn't you just do [] * 100? Maybe that's a thing in Python, I don't know. Seems more intuitive to me than [None] * 100.

No disrespect but you cannot make something ugly attractive. Alternatively, if something has natural beauty I would argue it would take extraordinary effort to make it ugly. Now, I would understand if something could be powerful and ugly at the same time and you work with what you got. Is Python powerful? My take is no.

Is Python ugly by design? Absolutely and you need only go as far as the constant self argument-stuffing to see this - one of the more bizarre characteristics of Python.

@grahamnicholls
Copy link

grahamnicholls commented Oct 15, 2021 via email

@bratwiz
Copy link

bratwiz commented Oct 15, 2021 via email

@juanfal
Copy link

juanfal commented Oct 15, 2021 via email

@grahamnicholls
Copy link

grahamnicholls commented Oct 15, 2021 via email

@xennex22
Copy link

Let's update this with a couple more observations:

  • No constants. No, a convention is not the same as a language feature.
  • Speed. Something between 100 and 400 times slower than C on the same hardware. The dogma tells people that the increase in productivity outweighs the decrease in execution speed. But if your embedded device runs at 0.1 FPS what good is that?
  • Resources. Uses 5 to 10 times more RAM than C. It's dynamic, so you find out at run time, not compile time.
  • Scalability. Large programs just turn into a giant mess.
  • Passing arguments by value vs reference is not intuitive with values of references being used.

@juanfal
Copy link

juanfal commented Dec 12, 2021

I don't care such much about speed issues. Of course, the faster the better, but that's not the goal for an interpreted scripting solve-problems language. You are not building an Operating System with Python or Ruby, but what is really poor and anarchic is the language itself, its grammar and inconsistencies. Long time there for it not to be refreshed with: constants, switch, better loops, more consistency in methods/functions, easier building of classes, stupid indentation (try it with more than 3 levels dropping to 0), etc, etc, etc

@greypanda
Copy link

This thread should be a book! It's hard to believe my first post was 2 years ago. I made some comments that were my opinion at the time, but more experience means more opinions.

I recently took on a project on a Raspberry PI. Python is considered one of the "ideal" languages for that, since C/C++ compilations can take hours to complete. Although I finished the project, I am not proud or pleased with it. xennex22 hit the Python nail on the head. I'm considering rewriting the app in C++ just to get it running reliably.

So have I completely abandoned Python? No. I think it is still a great tool for getting a basic concept running. You just put the code in a file and run it. But for a "real" application, it is just too clunky to bear.

There are way too many "blessed" libraries that are written by beginners and fail the stress tests. I find myself rewriting much of the code in libraries and just using the pieces I need. ( This is not much better than the libraries in Arduino, which have to be carefully weighed before they are trusted. But maybe they can be forgiven since they are free and written by and for hobbyists. )

I was originally impressed by pipenv since it did away with a lot of the confusion of venv, pip and friends. But then I have to ask "why do I need to have 5 different versions of Python running along with the crazy dependencies that presents?" That is one of the reasons I learned to hate Java -- it seemed like every program required a special minor version to even compile. Every time I forget to prefix a command with pipenv run, I cuss.

So since I am no longer paid to make stuff in the image of the program manager, I am really trying to find a language that is not ugly, performs well, and can run on just about any platform. Good luck to me.

@ekeyser
Copy link

ekeyser commented Dec 15, 2021

As was so concretely and colorfully illustrated by Shayne Oneill, the Python community has its fair share of zealots who find no value in the opinions of others. About the only other community that is/was similar is Perl but at least w Perl they don't pretend that their language isn't archaic or possibly even obtuse. I find it odd that even with so many people pointing out shortcomings with Python that die-hard Python developers will dismiss anyone with any amount of criticism with such extreme rhetoric. I guess everyone else is simply no on their level.

I guess the next topic is what programming language has what kinds of users and enumerating all behaviors and characteristics of each community. ;-)

@xennex22
Copy link

And here's some more reasons not to use Python

  • Energy inefficiency. A recent study shows that Python uses 75 times as much energy to accomplish the same goal as would a C program. Think of all the data centers with banks of servers running Python, and the amount of wasted energy that results in.
  • The stupid backward conditional expressions. I'm not sure who thought the assignment should be before the condition, but it makes reading code difficult (but still the dogma that python is simple and clean and easy to understand persists).
  • The reliance on third party tools to fix the broken language. Since dynamic languages can't find type problems until runtime (if at all) then you are told to run a code audit tool like pyrama. Of course, the linters are written in python so select the one you want to use carefully because they are slow. Same with the stupid reliance on white-space for scope - if you criticize it you are told that you should be using tool x (or even that it is somehow purer and therefore better than using braces).
  • The response from the python cultists, as demonstrated in this thread, that any criticism of python means that you are deficient as a programmer or you are doing something wrong or you just don't understand python or you are not using the right additional hack tool to fix the broken language.

@xennex22
Copy link

xennex22 commented Feb 5, 2022

Guido is an arrogant hack. He created a second rate scripting language which languished for a decade before by pure luck it was adopted by Google.

As an example, here is Guido's take on Python vs C++ (direct quotes):
"Python code is ... often 5-10 times shorter than equivalent C++ code"
and
"one Python programmer can finish in two months what two C++ programmers can't complete in a year"
and
Q: "Is C++ better than Python?" A: "If you have to ask, learn Python."

How arrogant do you have to be to claim with a straight face that a Python program is 5-10 times smaller than a C++ program? Or that programming productivity is 12 times high in Python than C++?

"An Empirical Comparison of Seven Programming Languages" finds that writing a program in a dynamic scripting language (Perl, Python, Rexx, Tcl) takes half as long as C/C++ and about half the length. The paper does not explain why the C and C++ programs were the same length and took the same time to write. The number of defects (ie quality) of each language was about the same. I would note that the example program was realitively small and involved string processing, something that favors dynamically typed scripting languages.

"A comparison of common programming languages used in bioinformatics" shows that Python was 90% the length of C++ and about 50% the length of C.

"On the Impact of Programming Languages on Code Quality" shows quality is about the same too.

So boom goes the dogma that Python programs are higher quality (one look at the source for most of the libraries would tell you that). And also we can reject the claims that Python programs are an order of magnitude smaller and an order of magnitude quicker to write.

@isnikulin
Copy link

Nice read, I have recently tried to delve into Python when learning algoritms and I was STUNNED by the fact that python doesn't have a simple one-line way to write an indexed for loop. Instead, every for is a for each with top-level range() or enumerate().
Baffling.

@grandslammer
Copy link

grandslammer commented Feb 10, 2022

I recently thought I'd give Python a try and was getting frustrated with several things about it, so I Googled and found this post. Here's what I really don't like about it, and these are really starting to put me off it entirely:

  • Multiple Python versions installed on my PC (forget about v2 vs v3, I'm talking about multiple versions of v3!)
  • Package management hell
  • Virtual environment hell
  • Whether to install vanilla Python (direct from the python.org website), or Anaconda - possibly both!
  • I'm beginning to get slight feelings of "this thing is trying to take over my PC!" (see points above)

Feels terrible from an organisational point of view.

With any other language I've used, if there's an update to the language, you just download it and it replaces the old version on your system. No messing around with multiple versions of the same language.
The same goes for updating packages - e.g. you just run "npm update" inside a project's folder for JavaScript, and boom! It's done.

Forgive me if there are easy fixes to the above, I'm still new to Python. I've looked and cannot find any resolutions anywhere though.

@ekeyser
Copy link

ekeyser commented Feb 10, 2022

That reminds me of the completely hilarious abandonment of pip search support....

xmlrpc.client.Fault: <Fault -32500: "RuntimeError: PyPI's XMLRPC API is currently disabled due to unmanageable load and will be deprecated in the near future. See https://status.python.org/ for more information.">

Yeah, like pip is the only thing on this planet that receives a lot of traffic. Embarrassing. "hundreds of thousands of search calls per hour". Awww, you poor little victim ;-)

ref
https://stackoverflow.com/questions/66375972/getting-error-with-pip-search-and-pip-install

@xennex22
Copy link

xennex22 commented Feb 14, 2022

  • The inability to retain backward compatibility between versions. Sure, languages like C++ evolve with standards, but I can take C source from 30 years ago written under C90 and it still compiles and works just fine under C++17. Backwards compatibility is a thing.
  • Assignment expressions. aka the walrus. To fix an original design mistake there now have to be two operators to do the same thing - assignment and expression assignment. No reason not have done it right in the first place.
  • Python only works in a narrow niche. Mobile, embedded, even desktop are not practical targets for Python. Why now? Because of the speed and memory requirements this language has.
  • Scoping was inefficient and when it got fixed it is now half broken. So you end up with things like a function being able to see variables from outside scope but not make changes to them. And Python is touted as the beginner language which is easy to understand. Not when there are gotchas like this.
  • Default arguments have a life and are mutable so become something other than the default value on subsequent calls. As above, why this non intuitive behavior?
  • Python makes a big deal about leaving out ++ and -- operators because they are 'not easy for beginners to understand' but includes +=
  • True and False are hacks for integer values
  • Lambdas are very restricted in that they have to be an expression and are limited to only one line. So you have to create a function and call that in your lambda expression, which more or less defeats the point of a lambda.
  • The development of a fix it culture around Python. eg Frustrated by incompatibility between Python versions for deployment? Use containers. Can't determine the logic flow if a program because of the invisible elements? Use an editor which shows tabs for you.
  • The hacks needed to cope with the 10000 faces of python versions. eg from __future__ import xxxxx. The only one I want to work does not work from __future__ import braces. And of course, it is not an import at all but uses the import keyword ie it is a hack. Also note the use of not one but two sets of double underscores.
  • The hacks needed to work with a dynamically typed language. eg how is / and // initiative to beginners? Also note that the introduction of the floating point division // broke old code. A cast is clearer because at least a beginner would have some idea what a float might be.
  • The 50 odd built in functions that break any object orientated structure to python. eg len(object) rather than object.len(). But then objects in Python are a cruel hack - looking at you self.
  • There is no forced structure to a python program. Thus people program all sorts of crazy things like mixing inline code along with code in functions. No wonder the quality of most libraries is low. Not everyone should write code that others depend on.
  • The prolific use of assert as a non debugging aid in release software. That would bring me on to the next problem with using Python in a commercial environment - the lack of a pre-processor.
  • Why is python written in C/C++ if C is so bad? You can implement a new language from any other by first writing an interpreter and using that to write a compiler. The interpreter is there, so why no compiler to compile the python written interpreter?
  • The more you look the clearer it is that python is an ugly hack of a language. The emperor has no clothes.

@isaax2
Copy link

isaax2 commented Feb 23, 2022

I'm happy that I'm not alone, I develop apps in since about 15 years in lots of languages and layers (C, C++, C#, Java, Objective-C, Swift, Javascript, LUA, Ruby ... etc). Now I took Python with an existing real project, and it's the first time that I can barely understand what's happening using the "simple and easy syntax".

@Odalrick
Copy link

map and enumerate do not have the same methods as list

Can someone explain to me exactly why completely different things should have the same methods?

And another thing:

single-item tuple (tuple,)

I know about the syntax for a single item tuple, and can even agree that it is an easy mistake to make; but for the lite of me I cant think of a single time when I'd actually need a single item tuple.

It's almost as if the thinks the only difference between tuples and list is that one is immutable; but no-one can be that silly can they?

Everyone knows tuples are fixed collections of heterogenous things and lists are variable homogenous collections.

@codingben
Copy link

@isaax2 Same here. I'm a developer with more than 10 years of coding - I never saw complicated language like Python, it's like a different world and very hard to keep it "simple" when your application become bigger and bigger (especially when it comes to PyTest, Typing, etc). Just see [1] how it's complicated.

I can confirm that Python is much much more complicated than Rust. 😄

[1] https://www.python.org/dev/peps/pep-0544

@Reenuay
Copy link

Reenuay commented Apr 9, 2022

Just learn functional statically typed language and you will understand that all those mainstream languages are the same sh*t. For example, you can learn Elm. After that I don't want ever write a project with a non-functional language.

@BrianG61UK
Copy link

BrianG61UK commented Apr 22, 2022

Dynamic Typing.
How can a language that doesn't identify type mismatches at compile time be considered anything other than a toy?
Mistakes in syntax can go unnoticed until they just happen to crash your program at some random point in it's life time.
It's almost as hopeless as the Microsoft Basic was on the first computer I owned as a teen back in the 80s.

@StitiFatah
Copy link

Dynamic Typing. How can a language that doesn't identify type mismatches at compile time be considered anything other than a toy. Mistakes in syntax can go unnoticed until they just happen to crash your program at some random point in it's life time. It's almost as hopeless as the Microsoft Basic was on the first computer I owned as a teen back in the 80s.

http://mypy-lang.org/

@tworthington
Copy link

tworthington commented May 8, 2022 via email

@BrianG61UK
Copy link

BrianG61UK commented May 9, 2022

I don't think I've had a runtime type mis-match error this century, and I mostly code in dynamically-typed languages. I suppose being able to check at compile time is perhaps a nice to have, where it can actually work, but if it's saving you from bugs routinely then you're doing something else wrong. Probably writing functions/blocks/methods which are too long so you lose track of what you're doing. Python has many flaws, but dynamic typing isn't one of them and adding it won't help.

You never have complicated global data structures and many people writing code that manipulates them?
When the data structures get modified to add new features, you never miss any of the changes needed in any module?

@nslay
Copy link

nslay commented Jun 23, 2022

I love how I can crash my Python scripts in ways you can programs written in native languages. Messages about segfaults and double-free corruption are things you wouldn't expect to see with Python. The worst I've seen to date is silent memory corruption... wow, I just never thought I'd see that in Python! Only knowledge about a native language can provide any clue as to why these kinds of crashes might have happened (and how to workaround or fix them in Python). A newbie computer user isn't going to understand, for example, numpy memory trickery and all the things that could go wrong if numpy doesn't account for shared memory correctly.

Maybe my gripes are more directed toward Python packages. Where do you draw the line between essential packages like numpy and Python, the language? I think numpy played a very big role in Python's popularity. Yet you can really shoot yourself in the foot in worse ways than described in this article. Like C levels of foot-shooting! And it's more mysterious because of how convenient and high-level the language is.

@rednnnno
Copy link

rednnnno commented Jul 1, 2022

The more I think about it, the more the forced spaced scopes remind me of the COBOL/RPG400 days...

I guess it's to be expected; we went from mainframes -> Personal computers -> cloud computing (really just back to mainframes - but on the internet now...)

So I guess it's just a fashion and it works exactly like fashion; concepts are cool for a few years; mostly forgotten about a pulled from the moth-balls back to relevance by some nostalgic geezers and/or some younguns that've never seen it before.

@masonova1
Copy link

masonova1 commented Jul 19, 2022

I want to leave this gem here:
Python equivalence to inline functions or macros (SO link)

Check the first answer, and witness the intersection of Python's optimization philosophy with Python's modularity philosophy.

It's somewhat old, but this sentiment with regard to performance problems still seems very present in the Python community. I'm convinced that this attitude will be the downfall of Python's industry prevalence, so long as none of the people at the round table can agree on how to improve this through language features instead of packages and modules.

Another problem I've noticed is that it is excruciating to convince Python developers in the first place that useful, ubiquitous concepts as simple as drop-in, expandable "macros" are deserving of optimized implementations, or even a language keyword or two. Every Pythonic solution I've heard to this, even when it's not just "don't worry about it", sounds astonishingly worse than just implementing the feature; As a programmer, I should be able to guarantee to at least my own interpreter that "yes, this static, unchanging macro with no state will be defined by the time you encounter something that needs it. If it hasn't, throw me some error about it."

With specific regard to the macros/inlining problem, I find arguments of the form "this is too complex to distinguish from regular functions for Joe Schmo the Python developer" to be implicitly patronizing to their own community and pretty unsatisfying as a justification for just not having anything like this.

Edit: I should add that the existence of the Inliner module is not even a solution to the specific problem as I stated it -- the Inliner does not "add macros/inlining to Python", it implements macros/inlining as a modular dependency. This is precisely the absurd collision of modularity philosophy and optimization philosophy that I hinted at earlier.

@ekeyser
Copy link

ekeyser commented Jul 26, 2022

That was entertaining and I can't wait for the python fanbois to be triggered. I agree indentation requirements are pure annoyance - basically forced training wheels. I'm not sure why any language forces indentation. Is it a sort of equivalent to logic/conditional/loops type-safety (or type-strictness)? That's my guess. "Oh, gotta make sure your if statements line up or you could do something bad!"

python2 and python3 are for all intents and purposes different languages. At least they differ enough and typically cannot be executed using the other interpreter without problems, errors, warnings that I just consider them to be completely different languages and yes a lot of this bleeds over into the packages and cross-python package incompatibilities. python3 should've been backward compatible with python2 - not sure why it wasn't. Feel like that was just yet another poor design decision.

Could the same be said for node/js and the different versions and standards there? I suspect so but w js you have what I believe is a standards body overseeing the publishing of the ECMAscript versions. And I believe (I could be wrong) you can run old js with new ECMAscript engines (or versions of node). With Python you just have an arbitrary decision by the language developers. And iirc there wasn't much of a deprecation or roadmap so what you ended up with was essentially 2 versions of every python app or one version or the other. I sort of have a hard time believing they couldn't maintain support for previous versions of the language.

Every language evolves over time but for some reason the Python way feels incredibly disjointed.

@fralligator
Copy link

fralligator commented Jul 30, 2022

dont use python
i only picked this shit language up because i need it for data science because physicists are fanboys of a shit language try coming from kotlin which is insanely fast, never get errors beautiful 20 years of learned errors corrected into the language from other languages then coming to python just to discover you never really know why you get an error it could be something utterly lame like simple spaces because of their utterly worthless scope decision O_o because they thought removing "{ }" or using ":" (omfg) would be a good idea they also use this utterly useless ____ case all the time with classes whereas camelcase utterly superior the type libraries is all messed up tbh fucking pain really hope kotlin destroys this worthless language or GO which is the next best alternative other than kotlin for data science

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment